Skip to content

[Tech Spec] PlaceToPay — VoidPostCapture #170

Description

@iemyashasvi

PlaceToPay — VoidPostCapture

Complexity: low
Generated by: Grace pipeline run run-2026-05-21T23-32-25-523Z-5aba32

Summary

Implement VoidPostCapture payment method for PlaceToPay connector. Technical Specification — PlaceToPay

Scope

Technical Specification — PlaceToPay

1. Connector Profile

Property Value
Connector Name PlaceToPay
Primary Flow Scope Card Payments (Authorize, Capture, Void, VoidPostCapture, Refund, PSync, RSync)
API Family REST/JSON
Production Host https://checkout.placetopay.com/rest/gateway
Sandbox Host https://test.placetopay.com/rest/gateway
Regional Variants None documented; single international endpoint

2. Authentication

Scheme: Custom HMAC-SHA256 digest (WS-Security style)

Every request body carries an auth object:

{
  "auth": {
    "login": "<merchantLogin>",
    "tranKey": "<base64(SHA256(nonce_bytes + seed + secretKey))>",
    "nonce": "<base64(random_16_bytes)>",
    "seed": "<ISO-8601 UTC timestamp, e.g. 2024-01-15T10:30:00+00:00>"
  }
}

Credentials required:

  • login — Merchant login identifier (plain string, sent as-is)
  • tran_key / secret — Merchant secret key used as the HMAC input (never sent directly)

Construction algorithm:

  1. Generate 16 cryptographically random bytes → nonce_bytes
  2. Base64-encode nonce_bytesnonce
  3. Format current UTC time as YYYY-MM-DDTHH:MM:SS+00:00seed
  4. Compute SHA256(nonce_bytes || seed_bytes || secret_bytes) (concatenation of raw bytes)
  5. Base64-encode the SHA256 digest → tranKey

Implementation notes:

  • nonce and seed are request-unique; replay is prevented by the timestamp.
  • The +00:00 suffix must be explicit (not Z).
  • There are no HTTP Authorization headers; auth lives entirely in the JSON body.

3. Supported Flows

Flow HTTP Method Path Notes
Authorize POST /process Card-present or card-not-present authorization
PSync POST /query Retrieve payment status by internalReference
Capture POST /transaction action=CHECKOUT — confirm/settle a pre-authorized payment
Void (pre-auth) POST /transaction action=VOID — ca

Out of Scope

Not specified in techspec

Technical Constraints

  • Follow existing connector patterns in the codebase

Full Tech Spec

Technical Specification — PlaceToPay

1. Connector Profile

Property Value
Connector Name PlaceToPay
Primary Flow Scope Card Payments (Authorize, Capture, Void, VoidPostCapture, Refund, PSync, RSync)
API Family REST/JSON
Production Host https://checkout.placetopay.com/rest/gateway
Sandbox Host https://test.placetopay.com/rest/gateway
Regional Variants None documented; single international endpoint

2. Authentication

Scheme: Custom HMAC-SHA256 digest (WS-Security style)

Every request body carries an auth object:

{
  "auth": {
    "login": "<merchantLogin>",
    "tranKey": "<base64(SHA256(nonce_bytes + seed + secretKey))>",
    "nonce": "<base64(random_16_bytes)>",
    "seed": "<ISO-8601 UTC timestamp, e.g. 2024-01-15T10:30:00+00:00>"
  }
}

Credentials required:

  • login — Merchant login identifier (plain string, sent as-is)
  • tran_key / secret — Merchant secret key used as the HMAC input (never sent directly)

Construction algorithm:

  1. Generate 16 cryptographically random bytes → nonce_bytes
  2. Base64-encode nonce_bytesnonce
  3. Format current UTC time as YYYY-MM-DDTHH:MM:SS+00:00seed
  4. Compute SHA256(nonce_bytes || seed_bytes || secret_bytes) (concatenation of raw bytes)
  5. Base64-encode the SHA256 digest → tranKey

Implementation notes:

  • nonce and seed are request-unique; replay is prevented by the timestamp.
  • The +00:00 suffix must be explicit (not Z).
  • There are no HTTP Authorization headers; auth lives entirely in the JSON body.

3. Supported Flows

Flow HTTP Method Path Notes
Authorize POST /process Card-present or card-not-present authorization
PSync POST /query Retrieve payment status by internalReference
Capture POST /transaction action=CHECKOUT — confirm/settle a pre-authorized payment
Void (pre-auth) POST /transaction action=VOID — cancel before capture
VoidPostCapture POST /transaction action=REVERSE with authorization — reverse a captured/settled transaction
Refund POST /transaction action=REVERSE with authorization — full reversal only (partial not supported)
RSync POST /query Retrieve refund/reversal status by internalReference
Webhooks N/A N/A Not implemented / not documented in current integration

VoidPostCapture vs Void distinction: PlaceToPay uses action=VOID to cancel a transaction that has been authorized but not yet captured. Once a transaction is captured (action=CHECKOUT), the only way to reverse it is action=REVERSE. The authorization code returned in the capture response must be included in the Reverse request.


4. Request Schema Highlights

Authorize — POST /process

{
  "auth": { "login": "", "tranKey": "", "nonce": "", "seed": "" },
  "payment": {
    "reference": "<connector_request_reference_id>",
    "description": "<order description>",
    "amount": {
      "currency": "USD",
      "total": 1000
    }
  },
  "instrument": {
    "card": {
      "number": "4111111111111111",
      "expiration": "12/26",
      "cvv": "123"
    }
  },
  "ipAddress": "<customer IP>",
  "userAgent": "<browser user agent>"
}
  • All fields are required for card payments.
  • amount.total is in minor units (cents).
  • expiration format: MM/YY (slash-delimited, 2-digit year).
  • ipAddress and userAgent come from browser_info in the authorize request.

Capture — POST /transaction

{
  "auth": { "login": "", "tranKey": "", "nonce": "", "seed": "" },
  "internalReference": 123456789,
  "action": "CHECKOUT"
}

Void (pre-auth) — POST /transaction

{
  "auth": { "login": "", "tranKey": "", "nonce": "", "seed": "" },
  "internalReference": 123456789,
  "action": "VOID"
}

VoidPostCapture — POST /transaction

{
  "auth": { "login": "", "tranKey": "", "nonce": "", "seed": "" },
  "internalReference": 123456789,
  "action": "REVERSE",
  "authorization": "<authorization_code_from_capture_response>"
}
  • internalReference: PlaceToPay's numeric transaction ID returned in the Authorize/Capture response (stored as connector_transaction_id). Must be parsed as u64.
  • authorization: The authorization code returned in the Capture response (connector_metadata). Required for post-capture reversal.
  • action values (enum, SCREAMING_SNAKE_CASE): REFUND, REVERSE, VOID, PROCESS, CHECKOUT

PSync / RSync — POST /query

{
  "auth": { "login": "", "tranKey": "", "nonce": "", "seed": "" },
  "internalReference": 123456789
}

Refund — POST /transaction

{
  "auth": { "login": "", "tranKey": "", "nonce": "", "seed": "" },
  "internalReference": 123456789,
  "action": "REVERSE",
  "authorization": "<authorization_code>"
}
  • Only full refunds are supported. Partial refund amount ≠ payment amount → NotSupported error.

5. Response Schema Highlights

Payment Response (Authorize / Capture / Void / VoidPostCapture)

{
  "status": {
    "status": "APPROVED"
  },
  "internalReference": 123456789,
  "authorization": "AUTH_CODE_STRING"
}
Field Type Notes
status.status enum (string) See Transaction Status Values below
internalReference u64 PlaceToPay's numeric transaction ID; used as connector_transaction_id
authorization string (optional) Authorization code; stored as connector_metadata; required for VoidPostCapture/Refund

Transaction Status Values:

status Value Maps To AttemptStatus
APPROVED Charged
OK Charged
FAILED Failure
REJECTED Failure
ERROR Failure
PENDING Pending
PENDING_VALIDATION Pending
PENDING_PROCESS Pending

Refund Response (Refund / RSync)

{
  "status": {
    "status": "REFUNDED"
  },
  "internalReference": 123456789
}

Refund Status Values:

status Value Maps To RefundStatus
OK Success
APPROVED Success
REFUNDED Success
FAILED Failure
REJECTED Failure
ERROR Failure
PENDING Pending
PENDING_PROCESS Pending
PENDING_VALIDATION Pending

6. Error Handling

Error Response Schema

{
  "status": {
    "status": "FAILED",
    "message": "<human readable message>",
    "reason": "<error code or reason>"
  }
}
HTTP Status status.status Cause
400 FAILED Malformed request, missing required fields
401 FAILED Invalid or expired authentication (bad login/tranKey/nonce)
404 FAILED internalReference not found
422 FAILED Business rule violation (e.g., attempting REVERSE on non-captured transaction)
500 FAILED Internal PlaceToPay server error
  • status.reason → mapped to ErrorResponse.code (falls back to NO_ERROR_CODE if absent)
  • status.message → mapped to ErrorResponse.message and ErrorResponse.reason (falls back to NO_ERROR_MESSAGE if absent)
  • connector_transaction_id is not populated in error responses

7. Webhooks / Async Notifications

Status: Not implemented in the current PlaceToPay connector integration.

  • No webhook subscription endpoint is configured.
  • No webhook signature verification logic exists.
  • Async state changes must be polled via PSync (POST /query).
  • The IncomingWebhook trait is implemented as a no-op stub.

Documented gaps:

  • PlaceToPay's API may support push notifications (webhooks) for production environments, but the integration relies solely on polling for payment status updates.
  • No retry policy, delivery format, or verification mechanism is documented or implemented.

8. References

  • Connector implementation: crates/integrations/connector-integration/src/connectors/placetopay.rs
  • Transformer types: crates/integrations/connector-integration/src/connectors/placetopay/transformers.rs
  • Sandbox base URL: https://test.placetopay.com/rest/gateway (from config/development.toml)
  • Production base URL: https://checkout.placetopay.com/rest/gateway (from config/production.toml)
  • Field probe data: data/field_probe/placetopay.json
  • PlaceToPay official documentation: https://docs.placetopay.com/ (not scraped; WebFetch not invoked per Phase 1c skip instruction)
  • Integration test spec: crates/internal/integration-tests/src/connector_specs/placetopay/

9. VoidPostCapture Implementation Notes

This section is specific to the VoidPostCapture flow being added.

Semantic distinction from pre-auth Void

Scenario PlaceToPay Action When Used
Cancel before capture VOID Payment authorized but not yet settled
Reverse after capture REVERSE Payment captured/settled; full reversal needed

Request construction for VoidPostCapture

  1. Retrieve connector_transaction_id from the router data — this is internalReference (parse as u64).
  2. Retrieve authorization from connector_metadata stored at capture time.
  3. Send POST to /transaction with action=REVERSE, internalReference, and authorization.

Response handling

  • Same PlacetopayPaymentsResponse struct as Capture/Void — reuse existing response type.
  • Status mapping follows the same PlacetopayTransactionStatus → AttemptStatus conversion.
  • A successful VoidPostCapture returns APPROVED or OK status.

Constraint

  • authorization code must be available from the preceding Capture response. If not present, the REVERSE request may be rejected by PlaceToPay.

10. API Call Sequences

Standard Authorize → Capture → VoidPostCapture

POST /process            → { internalReference: 123, authorization: "AUTH" }  [Authorize]
POST /transaction        → action=CHECKOUT, internalReference=123             [Capture]
POST /transaction        → action=REVERSE, internalReference=123, authorization="AUTH" [VoidPostCapture]

Authorize → Void (pre-auth)

POST /process            → { internalReference: 123 }   [Authorize]
POST /transaction        → action=VOID, internalReference=123 [Void]

11. Field Dependency Analysis

Field Source Required For Notes
auth.login ConnectorConfig.login All flows Merchant identifier
auth.tranKey Derived (SHA256 of nonce+seed+secret) All flows Recomputed per request
auth.nonce Random 16 bytes, base64 All flows Per-request uniqueness
auth.seed Current UTC timestamp All flows Tied to nonce for replay protection
internalReference Authorize response Capture, Void, VoidPostCapture, PSync, Refund, RSync Stored as connector_transaction_id
authorization Capture/Authorize response (connector_metadata) VoidPostCapture, Refund Required for post-capture operations
action Flow type Capture, Void, VoidPostCapture, Refund Enum: CHECKOUT/VOID/REVERSE
payment.reference connector_request_reference_id Authorize Merchant-side order reference
payment.amount.total minor_amount Authorize Minor unit currency amount
instrument.card.* PaymentMethodData::Card Authorize Card details
ipAddress browser_info.ip_address Authorize Required for card authorization
userAgent browser_info.user_agent Authorize Required for card authorization

12. UNDECIDED Fields

Field Location Question
authorization in VoidPostCapture Request body Confirm whether PlaceToPay requires authorization for REVERSE on all captured transactions, or only for specific card networks
Partial reversal VoidPostCapture Confirm whether PlaceToPay supports partial REVERSE amounts (currently assumed: no, same as Refund)
action=REFUND PlacetopayNextAction enum Distinguish from REVERSE — may be for partial amounts or different reversal semantics

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions