Home/Migration/S2S → Orchestration

Migrating from S2S to the Orchestration API

Field-by-field guide for merchants moving from the form-urlencoded S2S API to the JSON Orchestration API.

This guide is for merchants currently integrating directly with the S2S (server-to-server) API — the form-urlencoded endpoint at eu-test.oppwa.com / sandbox-card.peachpayments.com — and moving to the Orchestration API.

Endpoint mapping

S2SOrchestration APINotes
POST /v1/payments (paymentType=DB/PA)POST /paymentsAuthorise + capture (DB) or auth-only (PA — set capture_method: "manual")
POST /v1/payments/{id} (paymentType=CP)POST /payments/{id}/captureCapture a previously authorised PA
POST /v1/payments/{id} (paymentType=RV)POST /payments/{id}/cancelVoid (only works pre-capture)
POST /v1/payments/{id} (paymentType=RF)POST /refundsRefund a captured payment
GET /v1/payments/{id}GET /payments/{id}?force_sync=trueSync / retrieve
POST /v1/threeDSecurenot user-facingOrchestration handles standalone 3DS internally when authentication_type: "three_ds"
GET /v1/threeDSecure/{id}not user-facingSame — handled by Orchestration

Authentication mapping

S2SOrchestration API
Authorization: Bearer <token>api-key: <merchant_api_key> (per-merchant, not per-entity)
entityId=<entity-id> (form param)Configured on the merchant connector account (MCA): connector_account_details.key1
Bearer tokenConfigured on the MCA: connector_account_details.api_key, auth_type: "BodyKey"

You provision one MCA per S2S entity. If you have multiple entities (e.g. one per acquirer), create one MCA per entity and route between them with Orchestration's routing rules.


Field-by-field request mapping

Basic transaction fields

S2SOrchestration APINotes
amount=92.00 (major units, two decimals)"amount": 9200 (minor units, integer)Orchestration converts to S2S's major-unit string automatically
currency=ZAR"currency": "ZAR"
paymentBrand=VISAderived from card BIN, or "payment_method_type": "credit"/"debit"Orchestration picks the brand from the card number; payment_method_type controls credit vs debit routing
paymentType=DB"capture_method": "automatic" (default)DB = authorise + capture
paymentType=PA"capture_method": "manual"Authorise only; capture later
merchantTransactionId=ord_123"connector_request_reference_id": "ord_123" (or payment_id)Orchestration truncates to S2S's 16-char limit
descriptor=Coffee"statement_descriptor_name": "Coffee"
testMode=EXTERNALMCA connector_meta_data: { "test_mode": "EXTERNAL" }Opt-in. Default (absent) → S2S's INTERNAL default.

Card details

S2SOrchestration API
card.number=4111111111111111"payment_method_data.card.card_number": "4111111111111111"
card.holder=Jane Jones"payment_method_data.card.card_holder_name": "Jane Jones"
card.expiryMonth=05"payment_method_data.card.card_exp_month": "05"
card.expiryYear=2034"payment_method_data.card.card_exp_year": "34" (or "2034")
card.cvv=123"payment_method_data.card.card_cvc": "123"

payment_method is "card", payment_method_type is "credit" or "debit".

Wallets

S2SOrchestration API
applePay.paymentToken=..."payment_method_data.wallet.apple_pay_redirect": {} (browser SDK) or predecrypt token via Apple Pay decryption flow
googlePay.paymentToken=..."payment_method_data.wallet.google_pay_redirect": {}
samsungPay.paymentToken=..."payment_method_data.wallet.samsung_pay": { "token": "..." }
paymentBrand=APPLEPAY"payment_method_type": "apple_pay"
paymentBrand=GOOGLEPAY"payment_method_type": "google_pay"
paymentBrand=SAMSUNGPAY"payment_method_type": "samsung_pay"

Customer

S2SOrchestration API
customer.email=jane@x.com"email": "jane@x.com"
customer.givenName=Jane"billing.address.first_name": "Jane"
customer.surname=Jones"billing.address.last_name": "Jones"
customer.merchantCustomerId=cust_42"customer_id": "cust_42"
customer.ip=1.2.3.4Auto-set by Orchestration from the request IP. Override with "browser_info.ip_address": "1.2.3.4" if needed
customer.phone=+27115551234"phone": "+27115551234"

Browser info (3DS 2.x)

S2SOrchestration API (browser_info)
customer.browser.acceptHeaderaccept_header
customer.browser.languagelanguage
customer.browser.screenHeightscreen_height
customer.browser.screenWidthscreen_width
customer.browser.timezonetime_zone
customer.browser.userAgentuser_agent
customer.browser.javascriptEnabledjava_script_enabled
customer.browser.javaEnabledjava_enabled
customer.browser.screenColorDepthcolor_depth / screen_color_depth
customer.browser.challengeWindow(Orchestration sets internally; S2S receives 03 for full-screen)

Billing / shipping address

S2SOrchestration API
billing.street1billing.address.line1
billing.street2billing.address.line2
billing.citybilling.address.city
billing.statebilling.address.state
billing.postcodebilling.address.zip
billing.country=USbilling.address.country: "US"
shipping.*shipping.address.* (same field names as billing)

Use-case examples

S2S's standingInstruction.*, createRegistration, and threeDSecure.* fields are derived by Orchestration from the shape of your request — you don't set them directly. Pick a use case below to see the before/after.

Save a card during a customer-present payment so it can be charged later.

S2S — before
http
Loading...
Orchestration — after
POST /payments
Loading...

Orchestration sends createRegistration=true and the correct standingInstruction.* fields automatically.

Response (excerpt)

json
Loading...

Store mandate_id for subsequent MITs. connector_mandate_id is S2S's registrationId, and network_transaction_id is the CITI / traceId for Nedbank acquirers — both are stored by Orchestration automatically.


Custom parameters / metadata

S2S accepts arbitrary customParameters[key]=value form fields.

S2SOrchestration APIWhat Orchestration forwards
customParameters[paymentId]=pay_xxx(set automatically by Orchestration)
customParameters[your_key]=your_value"metadata": { "your_key": "your_value" } (top-level)not yet auto-forwarded — see below

Response field mapping

S2S response fieldOrchestration API response field
idconnector_transaction_id
result.codeclassified into status (succeeded / processing / failed) + error_code
result.descriptionerror_message
result.cvvResponsepayment_method_data.card.payment_checks (CVV part of the JSON)
registrationIdconnector_mandate_id
paymentBrandpayment_method_data.card.card_network
card.binCountryincluded in connector response data
resultDetails.AuthCode (or ApprovalCode fallback)payment_method_data.card.auth_code
resultDetails.AcquirerResponsepayment_method_data.card.payment_checks (acquirer part)
resultDetails.MerchantAdviceCodeexposed in connector response data
resultDetails.ConnectorTxID2 (Nedbank) → RRN at position 2connector_response_reference_id
resultDetails.ConnectorTxID3 (Nedbank) → CITI at position 4network_transaction_id
STAN / originalTransactionId parsed from ConnectorTxIDspayment_method_data.card.payment_checks
risk.scoreincluded in connector response data
threeDSecure.eci / verificationId / dsTransactionId / acsTransactionId / version / flowauthentication_data
standingInstruction.agreementIdpersisted in mandate_metadata for replay on next MIT

What you stop doing

You no longer need to:

  • track registrationId, agreementId, or initialTransactionId yourself — Orchestration persists them in the mandate
  • compute and send standingInstruction.mode/source/type/initiator/transactionType — Orchestration derives them from the flow
  • maintain entity-specific routing logic — set up an MCA per entity and let Orchestration routing rules decide
  • handle 3DS redirects manually for the standard flow — Orchestration exposes a single next_action.redirect_to_url and a single redirect-back URL
  • parse result.code to classify success/pending/failure — Orchestration maps codes to status and error_code/error_message

You still need to:

  • decide capture_method (auto vs manual) per transaction
  • decide mit_category for subsequent MITs
  • pass setup_future_usage: "off_session" + customer_acceptance on the initial CIT to mint a mandate
  • pass external-3DS data via three_ds_data if you do your own 3DS
  • store payment_id and mandate_id returned by Orchestration

Minimum testing checklist

  1. No-3DS card payment, auto-capture — issues a successful payment and gets a connector_transaction_id.
  2. Manual capturecapture_method: "manual"requires_capturePOST /payments/{id}/capture.
  3. RefundPOST /refunds against a succeeded payment.
  4. Initial CIT (off-session)setup_future_usage: "off_session" + customer_acceptance returns a mandate_id + connector_mandate_id + network_transaction_id.
  5. Subsequent MITrecurring_details.mandate_id returns succeeded.
  6. 3DS challengeauthentication_type: "three_ds" returns requires_customer_action with next_action.redirect_to_url; complete the redirect; payment becomes succeeded.
  7. External 3DS — pre-authenticated payment with three_ds_data succeeds without further user action.
  8. MIT after CIT — verify the second MIT against a CIT succeeds (proves agreementId, traceId replay).