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 (for example, 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).