Peach Payments
This page provides an overview of the payments methods provided by the Peach Payments adapter in the IXOPAY platform. It also includes a full list of all configuration options available to you when integrating Peach Payments within your payments landscape, as well as an overview of the parameters required when submitting a transaction via IXOPAY's API.
Peach Payments is a payment service provider for Africa, built on the OPPWA platform. The Peach connector processes redirect-based alternative payment methods via Peach's hosted Checkout (v2) page for South Africa and Mauritius, and real-time EFT payouts to South African bank accounts via Peach's Payouts API.
Supported features
Payment methods
The table below lists the payment methods that Peach Payments supports. It also includes the available processing options and the supported transaction types for each of those payment methods.
| Payment method | Processing options | Transaction types | |
|---|---|---|---|
PayShap (PayShap) | Hosted payment pages Full-page redirect | Debit Refund | Currencies: ZAR Countries: ZA |
MCB Juice (MCBJuice) | Hosted payment pages Full-page redirect | Debit | Currencies: MUR Countries: MU |
Blink by Emtel (BlinkByEmtel) | Hosted payment pages Full-page redirect | Debit | Currencies: MUR Countries: MU |
EFT (Eft) | Server-to-server | Payout | Currencies: ZAR Countries: ZA |
Currencies
Peach Payments supports the following currencies: ZAR, MUR.
Refunds
Refunds are available for PayShap only. Peach does not refund MCB Juice or Blink by Emtel payments — refund those manually.
- Peach allows one refund per PayShap payment, either full or partial. After a partial refund, no further refund is possible; Peach rejects a second refund attempt.
- Refunds go through Peach Checkout's refund API and are signed with the Checkout secret token. Copy it from the Peach Payments Dashboard (Checkout → API keys) into the connector option Checkout secret token. It is not the Webhook Secret, nor the Client Secret. The connector's Entity ID is used. Refunds fail with a configuration error until the secret token is set.
- A refund may complete asynchronously. It then stays pending until Peach's refund webhook reports the final state (see Webhooks).
- If the outcome of a refund request is unknown (connection failure or an unreadable answer from Peach), the refund is marked failed and flagged as not to be resubmitted, because Peach may already have refunded. Check the Peach dashboard before refunding again.
Payouts
Payouts are available on the EFT payment method only: real-time EFT to a South African bank account, in ZAR. Peach does not pay out to PayShap, MCB Juice or Blink by Emtel. Any other currency is rejected before the request reaches Peach.
- Credentials. Payouts use the Peach Payouts API, which Peach may issue separate credentials for (Peach Payments Dashboard, Payouts → Settings). Enter them into the connector options Payouts API Client ID, Payouts API Client Secret and Payouts API Merchant ID. Each option left empty falls back to its Checkout counterpart (Client ID, Client Secret, Merchant ID).
- Payout webhook secret. Payout webhooks are signed by Peach's webhook provider (Svix). The
signing secret (
whsec_…) is provided by Peach support and goes into the connector option Payouts webhook signing secret. There is no fallback for it. With Webhook signing Enabled (the default), payouts fail with a configuration error until it is set. - Beneficiary. The bank account is passed in the payout's
extraData(see Payout request mapping). The gateway does not validate it. Peach rejects a missing or malformed field with a per-field validation error, which is returned in the error message under gateway error code1002. - Duplicate protection. The Peach payout id is derived from the gateway transaction, so
resubmitting the same transaction can never pay twice. If Peach already holds a payout for the
transaction (result code
2900.003.002), the transaction stays pending and is resolved by the payout webhook. - Final state. A payout is often still in progress when Peach acknowledges it. It then stays
pending until the payout webhook reports the final state, with a server-side status poll as
fallback. A payout Peach later reports as
reversed(funds returned to the merchant's float) is turned into an error, also when it had already succeeded. - Unknown outcome. If the outcome of a payout request is unknown (connection failure, HTTP
5xx, or an answer without payout id or status), the payout is not failed, because Peach may already have paid. It stays pending until the payout webhook resolves it. Check the Peach dashboard before paying out again under a new transaction. Such a payout cannot be resolved by the status poll. Always configure the payout webhook secret when you use payouts: without it (only possible with Webhook signing Disabled), such a payout stays pending until resolved manually.
Mapping to Peach Payments
Parameters
Request mapping
| Gateway parameter | Peach Payments parameter | Type | Description |
|---|---|---|---|
amount | amount | required number | Transaction amount (major units, two decimals) |
currency | currency | required string | ISO 4217 currency code |
successUrl | shopperResultUrl | required string | Shopper return URL after completing the payment |
cancelUrl | cancelUrl | optional string | Shopper return URL after cancelling the payment |
customer.identification | customer.merchantCustomerId | optional string | Merchant-side customer identifier |
customer.firstName | customer.givenName | optional string | Customer first name |
customer.lastName | customer.surname | optional string | Customer last name |
customer.billingPhone | customer.mobile | optional string | Customer mobile number |
customer.email | customer.email | optional string | Customer email address |
customer.nationalId | customer.idNumber | optional string | Customer national ID number |
customer.billingAddress1 … | billing.street1 … | optional string | Billing address fields |
customer.shippingAddress1 … | shipping.street1 … | optional string | Shipping address fields |
Response mapping
| Gateway parameter | Peach Payments parameter | Type | Description |
|---|---|---|---|
purchaseId (adapter transaction id) | id | string | Peach payment unique id (from the status response) |
| second adapter transaction id | checkoutId | string | Peach checkout session id |
purchaseId of a refund | id | string | Peach id of the refund itself (not of the refunded payment) |
purchaseId of a payout | payouts[].payoutId | string | Peach payout id |
| second adapter transaction id of a payout | payoutRequestId | string | Peach payout request id |
Payout request mapping
| Gateway parameter | Peach Payments parameter | Type | Description |
|---|---|---|---|
amount | payouts[].amount | required number | Payout amount (sent to Peach in cents) |
currency | payouts[].currency | required string | Must be ZAR |
extraData.psp:bankName | payouts[].bankName | required string | Bank name as Peach lists it, e.g. ABSA, STANDARD BANK |
extraData.psp:accountNumber | payouts[].accountNumber | required string | Beneficiary account number |
extraData.psp:branchCode | payouts[].branchCode | required string | The bank's six-digit universal branch code |
extraData.psp:accountHolder | payouts[].accountHolder | optional string | Account holder name. Defaults to customer.firstName and customer.lastName |
description | payouts[].reference | optional string | Statement reference shown to the beneficiary. Falls back to merchantTransactionId. Reduced to letters, digits and spaces, at most 20 characters |
Error codes
Payments and refunds
| Gateway error code | Peach Payments error code | Peach Payments error message |
|---|---|---|
1002 (invalid request data) | HTTP 400 / 404, OPPWA validation codes (e.g. 100.800.*, 200.1*–200.3*) | Invalid request body / Channel not found |
1007 (invalid configuration) | HTTP 401, 600.2*, 500.1*, 500.2*, 800.121* | Access denied / Configuration error at Peach |
2003 (transaction declined) | HTTP 402, 800.100.* and others | Transaction declined |
2002 (user cancelled) | 100.396.101, 100.396.102, 100.396.104, 100.396.106, 100.397.101 | Cancelled by user |
2005 (transaction expired) | 100.396.103 | Transaction timed out |
1003 (processing error) | HTTP 5xx, 900.100–900.400, 800.5*, 800.800.8*, 600.1*, 999.*, other 100.395.*–100.397.* | Temporary failure at Peach |
Currency and payment method mismatches are rejected with 1002 before the request reaches Peach.
Payouts
| Gateway error code | Peach Payments error code | Peach Payments error message |
|---|---|---|
2006 (insufficient funds) | 2900.002.000, 2900.002.001 | Insufficient merchant float balance at Peach |
2003 (transaction declined) | other 2900.002.* | Payout declined |
1002 (invalid request data) | 2900.003.*, 2900.004.* | Validation error |
1007 (invalid configuration) | 2900.004.007, 2900.005.* | Payouts not enabled / IP address not allowed |
2007 (incorrect payment info) | 2001.002.*, 2001.003.* | Beneficiary account cannot receive funds |
1003 (processing error) | 2901.* and unknown codes | Technical error at Peach |
Webhooks
The connector consumes Peach's webhooks, so a payment reaches a final state even when the shopper never returns from their banking app. Peach delivers on two channels, both arriving on the same webhook URL and both supported:
- Encrypted OPPWA webhooks ("new style"): the payload is AES-256-GCM encrypted; the connector's Webhook Secret is the hex decryption key from the Peach dashboard. These are always decrypted and authenticated — the Webhook signing setting below does not apply to this channel.
- Checkout webhooks: a plaintext payload authenticated by HMAC-SHA256 signature headers
(
x-webhook-*). The Webhook Secret is the HMAC key exactly as shown in the Peach dashboard — it looks base64-encoded but must be entered verbatim, never decoded.
Setup:
- Register the connector's generic postback URL in the Peach Payments Dashboard (Webhooks):
https://<gateway-host>/postback/<connectorGuid>. Peach accepts one webhook URL per account. - Enter the secret from the Peach dashboard into the connector's Webhook Secret field.
- Dashboard settings: Types = PAYMENTS; the "Wrapper" setting may be left at either value (both the bare and the JSON-wrapped body form are accepted). Newly created webhooks are inactive until tested and activated in the dashboard.
DB(debit) andRF(refund) webhooks change transaction state. A refund webhook is matched to the refund by the refund's own Peach id. Reversal webhooks (RV) and REGISTRATION, SCHEDULE and RISK notifications are acknowledged and ignored.
Payout webhooks are a third channel on the same URL. They are signed by Svix (webhook-*
headers) and verified with the Payouts webhook signing secret. They are only applied when
the signature is valid. The Webhook signing setting does not switch this check off.
Webhook signing
The Webhook signing connector option controls signature verification on the Checkout webhook channel:
- Enabled (the default, also used when the option is not set): the
x-webhook-*HMAC signature headers are verified against the Webhook Secret; unsigned or invalidly signed deliveries are rejected. Requires webhook signing to be enabled in the Peach dashboard, and a webhook secret to be configured on the connector — transactions fail fast with a configuration error if the secret is missing. - Disabled: Checkout webhooks are accepted without any authentication (Peach accounts that never enabled signing in the dashboard send no signature headers at all). Only set this if signing genuinely cannot be enabled at Peach — with verification disabled, anyone who knows the postback URL can submit forged status notifications — for refunds as well as payments. Encrypted OPPWA webhooks remain authenticated regardless of this setting.
For payouts, the setting only decides what happens without a payout webhook secret: with Enabled, payouts are refused until the secret is set. With Disabled, payout webhooks are ignored, and pending payouts are resolved by the status poll only.
Miscellaneous
- The connector completes transactions via the Peach Checkout status call when the shopper returns, via webhooks (see above), plus a server-side status poll as a last-resort fallback for shoppers who never return.
- The domain initiating the checkout must be added to the allowlist in the Peach Payments Dashboard; it is configured on the connector ("Whitelisted domain").