Checkout webhooks

Overview

Webhooks are HTTP callbacks that deliver notification messages for events. Checkout sends a webhook to the merchant-provided URL for every state change of debit (DB), preauthorisation (PA), and refund (RF) requests.

📘

The API:

StateDescription
createdA Checkout instance has launched and the Checkout session initiated.
pendingThe merchant initiated a Checkout payment and is awaiting customer completion. Payments can have a future state depending on the customer's action.
successfulOne of the following has occurred:
- The customer completed a Checkout payment. A final state of a payment attempt.
- The merchant refunded a customer successfully. A final state.
uncertainThe customer might have cancelled a Checkout payment. Alternatively, a Checkout payment has timed out. The customer might not have completed their payment in the allotted time (30 minutes) or could have closed the payment window on their device. A final state of a payment attempt.
cancelledThe customer cancelled a Checkout payment. A final state of a payment attempt.
📘

In certain circumstances, Peach Payments could update a checkout status using webhooks after a checkout session ends. For example, system issues might result in a checkout status being incorrect during the session; when the session ends and the system recovers, Peach Payments sends a webhook with the updated status.

Transaction statuses can change as follows:

  • Created -> Pending
  • Pending -> Successful, cancelled, or uncertain
  • Uncertain or cancelled -> Successful

Peach Payments cannot guarantee the order of webhooks. For example, if your customer initiates a transaction and Peach Payments sends a pending webhook but you have a system issue that causes the webhook to go into exponential backoff, Peach Payments might send the next webhook before the pending webhook gets retried. The webhook payload contains a timestamp that you can use to identify the correct order of webhooks.

Configuration

Checkout sends the initial configuration webhook as JSON and all subsequent webhooks as x-www-form-urlencoded content.

You can configure webhooks in two ways:

  • Add a webhook URL in the Peach Payments Dashboard, as described in the Configure a webhook section.
  • Enter a webhook URL in the notificationUrl parameter of the initiate Checkout request:
    • You can configure a different URL to the webhook URL if you want to send webhooks to two locations.
    • You can set the notificationUrl dynamically per Checkout, for example, if you want to send all webhooks to the webhook URL, and specific webhooks for certain types of Checkouts to a different URL.
    • If you've configured a webhook in the Dashboard, Peach Payments sends webhooks to both the webhook URL and this URL.

Flow

Webhooks work as follows:

Webhook flow.

  1. The merchant receives a webhook with a result code indicating the updated checkout status.
  2. The merchant returns a 200 status code response acknowledging the webhook.

Webhook security

You can secure your webhook endpoints using HMAC SHA256 signing. When you enable webhook signing, Peach Payments signs each webhook payload so that you can verify that a request genuinely comes from Peach Payments and that no one has tampered with it in transit.

Enable webhook signing

📘

Only certain roles can enable webhook signing.

You can enable webhook signing from the Peach Payments Dashboard:

  1. Log in to your Peach Payments Dashboard and configure a webhook.
  2. Under Webhook security, click the Enable webhook signing toggle to the on position.
  3. In the Enable webhook signing window, copy the shared secret key that Peach Payments uses to sign your webhooks, then click Enable.

You can view and copy the secret key again at any time under Webhook security. You need this key to verify webhook signatures.

After you enable webhook signing, you can:

  • Disable webhook signing. Click the Enable webhook signing toggle to the off position, then click Disable. Peach Payments stops signing your webhook payloads.
  • Regenerate the secret key. Under Webhook security, click Regenerate secret key. Update your integration with the new key, because Peach Payments signs webhooks with the new key immediately.

If you disable and later re-enable signing without regenerating the key, Peach Payments uses your existing configured secret key.

Verify webhook signatures

After you enable webhook signing, each webhook request includes x-webhook-signature-algorithm, x-webhook-timestamp, x-webhook-id, and x-webhook-signature headers. You must verify the authenticity of the request by recalculating the signature using your secret key and comparing it to the value that Peach Payments sends.

When you recalculate the signature:

  • Use the raw request body exactly as you receive it. Checkout sends the body as form-urlencoded data, whereas Payment Links sends it as JSON.
  • Ensure that the URL matches the webhook URL that you configured on the Peach Payments Dashboard.

Peach Payments includes the webhook ID in the signature message to:

  • Prevent replay attacks by tying the signature to a specific webhook instance.
  • Enable correlation between merchant logs and Peach Payments logs for troubleshooting.
  • Allow merchants to verify the integrity of the webhook ID for idempotency checking.

The following code snippet shows how to verify the HMAC signature in Node (remember to replace placeholder values with your own values):

const crypto = require('crypto');

const timestamp = get('request.header.x-webhook-timestamp');
const webhookId = get('request.header.x-webhook-id');
const receivedSignature = get('request.header.x-webhook-signature');
const url = get('request.url'); // Must match your configured webhook URL
const payload = get('request.content'); // Raw body: form-urlencoded for Checkout, JSON for Payment Links

const secret = 'some-secret'; // Must match Peach Payments secret

const message = `${timestamp}.${webhookId}.${url}.${payload}`

const calculatedSignature = crypto
  .createHmac('sha256', secret)
  .update(message)
  .digest('hex');

const isValid = calculatedSignature === receivedSignature;

if (isValid) {
  console.log('✅ HMAC Signature Valid');
  console.log(`Timestamp: ${timestamp} (${new Date(timestamp * 1000).toISOString()})`);
  console.log(`Webhook ID: ${webhookId}`);
  console.log(`Signature: ${calculatedSignature.substring(0, 16)}...${calculatedSignature.substring(48)}`);
} else {
  console.log('❌ HMAC Signature Invalid');
  console.log(`Expected: ${calculatedSignature}`);
  console.log(`Received: ${receivedSignature}`);
  console.log(`Message: ${message.substring(0, 100)}...`);
}

set('hmac_valid', isValid);
set('hmac_signature', calculatedSignature);

Webhook retry mechanism

Peach Payments expects a 200 HTTP for successful webhook delivery and a non-200 HTTP status code for failures.

For an unsuccessful response, Peach Payments retries the webhook for 30 days or until a successful acknowledgement (200 HTTP) occurs.

Exponential backoff retry

Below is the exponential backoff logic.

The interval between retries is:

  • 2 minutes
  • 4 minutes
  • 8 minutes
  • 15 minutes
  • 30 minutes
  • 1 hour, every day until 30 days have passed since the first attempt

Webhook events

When your service receives a webhook notification, it must return a 200 HTTP status code. Peach Payments sends notifications using the URL-encoded query string format via HTTP POST. The webhook payload differs depending on the request parameters and payment method. If your service does not return a 200 HTTP status code, the webhook service considers the notification delivery as failed and retries sending the notification later.

Checkout created

Peach Payments sends this webhook when creating a checkout instance.

ParameterTypeExampleDescription
amountString (^[0-9]{1,8}(\\.[0-9]{2})?$)1010.22The amount of the payment request. The period is the decimal separator. M-PESA does not support decimal amounts, so Checkout automatically rounds them up.
checkoutIdString (64)948cc8dec52a11eb85290242ac130003Checkout ID.
currencyString enum [ZAR, USD, KES, MUR, GBP, EUR]ZARThe currency code of the payment request amount.
merchant.nameStringPeach PaymentsThe merchant name.
merchantTransactionIdString (8-16)OrderNo453432Merchant-provided reference number unique for your transactions.
paymentTypeString enum [DB, PA]DBThe payment type for the request.
result.codeString000.200.100A code representing the checkout state.
result.descriptionStringsuccessfully created checkoutA friendly message.
signatureString (64)a668342244a9c77...32035ed06164f4Token to verify the integrity of the webhook, ensuring the request is coming from Checkout.
timestampString2023-07-04T08:10:05ZDate and time when Peach Payments sent the webhook.

Checkout pending

Peach Payments sends this webhook when creating a transaction on a checkout instance.

ParameterTypeExampleDescription
amountString (^[0-9]{1,8}(\\.[0-9]{2})?$)1010.22The amount of the payment request. The period is the decimal separator. M-PESA does not support decimal amounts, so Checkout automatically rounds them up.
checkoutIdString (64)948cc8dec52a11eb85290242ac130003Checkout ID.
currencyString enum [ZAR, USD, KES, MUR, GBP, EUR]ZARThe currency code of the payment request amount.
idString8ac7a4a184123d86018413e098f32191The transaction ID.
merchant.nameStringPeach PaymentsThe merchant name.
merchantTransactionIdString (8-16)OrderNo453432Merchant-provided reference number unique for your transactions.
paymentBrandString enum [VISA, MASTER, DINERS, AMEX, MASTERPASS, MOBICRED, MPESA, 1FORYOU, APLUS, PAYPAL, ZEROPAY, PAYFLEX, BLINKBYEMTEL, CAPITECPAY, MCBJUICE, PEACHEFT, RCS, GOOGLEPAY, FLOAT, SAMSUNGPAY, HAPPYPAY, MAUCAS, MONEYBADGER, PAYSHAP, ABSAEFT]VISAThe payment method which the customer is paying with.
paymentTypeString enum [DB, PA]DBThe payment type for the request.
result.codeString000.200.000A code representing the checkout state.
result.descriptionStringtransaction pendingA friendly message.
signatureString (64)a668342244a9c77...32035ed06164f4Token to verify the integrity of the webhook, ensuring the request is coming from Checkout.
timestampString2023-07-04T08:10:05ZDate and time when Peach Payments sent the webhook.

Checkout successful

Peach Payments sends this webhook when a user completes a checkout instance.

ParameterTypeExampleDescription
amountString (^[0-9]{1,8}(\\.[0-9]{2})?$)1010.22The amount of the payment request. The period is the decimal separator. M-PESA does not support decimal amounts, so Checkout automatically rounds them up.
billing.cityString (optional)Cape TownThe customer's billing city.
billing.companyString (optional)Peach PaymentsThe customer's billing company.
billing.countryString (optional)ZAThe customer's billing country.
billing.postcodeString (optional)8000The customer's billing postal code.
billing.street1String (optional)123 Main StreetThe customer's billing street.
card.binString (optional)455112The first six digits of the card number.
card.expiryMonthString (optional)11The expiry month of the card.
card.expiryYearString (optional)2029The expiry year of the card.
card.holderString (optional)Grace NkosiThe card account holder.
card.last4DigitsString (optional)2315The last four digits of the card number.
schemeTransactionIdString (optional)ABCDEF1234567Scheme-level transaction identifier for tracking and reconciliation. The webhook contains schemeTransactionId or cardholderInitiatedTransactionId, depending on your bank.
cardholderInitiatedTransactionIdString (optional)123456789012345Cardholder-initiated transaction identifier for tracking and reconciliation. The webhook contains schemeTransactionId or cardholderInitiatedTransactionId, depending on your bank.
checkoutIdString (64)948cc8dec52a11eb85290242ac130003Checkout ID.
currencyString enum [ZAR, USD, KES, MUR, GBP, EUR]ZARThe currency code of the payment request amount.
customer.emailString (optional)[email protected]The customer's email address.
customer.givenNameString (optional)JohnThe customer's first name.
customer.mobileString (optional)+27831234567The customer's mobile number.
customer.surnameString (optional)DoeThe customer's surname.
idString8ac7a4a184123d86018413e098f32191The transaction ID.
isApplePayWalletBoolean (optional)trueSpecifies whether this is an Apple Pay transaction.
merchant.nameStringPeach PaymentsThe merchant name.
merchantTransactionIdString (8-16)OrderNo453432Merchant-provided reference number unique for your transactions.
paymentBrandString enum [VISA, MASTER, DINERS, AMEX, MASTERPASS, MOBICRED, MPESA, 1FORYOU, APLUS, PAYPAL, ZEROPAY, PAYFLEX, PAYBYBANK, BLINKBYEMTEL, CAPITECPAY, MCBJUICE, PEACHEFT, RCS, GOOGLEPAY, FLOAT, SAMSUNGPAY, HAPPYPAY, MAUCAS, MONEYBADGER, PAYSHAP, ABSAEFT]VISAThe payment method which the customer is paying with.
paymentTypeString enum [DB, PA, RF]DBThe payment type for the request.
recon.authCodeString123456The authorisation code from the payment service provider.
recon.resultCodeStringThe result code from the payment service provider.
recon.rrnString123456789012The reconciliation reference number from the payment service provider.
result.codeString000.000.000A code representing the checkout state.
result.descriptionStringtransaction pendingA friendly message.
resultDetails.AcquirerResponseStringApproved
resultDetails.ConnectorTxID1String8ac7a4a184123d86018413e098f32191Extra field for a transaction-related reference.
resultDetails.ExtendedDescriptionStringPurchase Approved OK
shipping.cityString (optional)Cape TownThe customer's shipping city. Populated for Embedded Express when you enable requiresShipping and the customer supplies the information.
shipping.companyString (optional)Peach PaymentsThe customer's shipping company.
shipping.countryString (optional)ZAThe customer's shipping country. Populated for Embedded Express when you enable requiresShipping and the customer supplies the information.
shipping.postcodeString (optional)8000The customer's shipping postal code. Populated for Embedded Express when you enable requiresShipping and the customer supplies the information.
shipping.stateString (optional)Western CapeThe customer's shipping state, province, or region. Populated for Embedded Express when you enable requiresShipping and the customer supplies the information.
shipping.street1String (optional)123 Main StreetThe customer's shipping street. Populated for Embedded Express when you enable requiresShipping and the customer supplies the information.
shipping.street2String (optional)Apartment 4The customer's shipping street (second line, for example, an apartment or unit number).
signatureString (64)a668342244a9c77...32035ed06164f4Token to verify the integrity of the webhook, ensuring the request is coming from Checkout.
timestampString2023-07-04T08:10:05ZDate and time when Peach Payments sent the webhook.

Checkout uncertain status

Peach Payments sends this webhook when a user might have cancelled a checkout instance.

ParameterTypeExampleDescription
amountString (^[0-9]{1,8}(\\.[0-9]{2})?$)1010.22The amount of the payment request. The period is the decimal separator. M-PESA does not support decimal amounts, so Checkout automatically rounds them up.
checkoutIdString (64)948cc8dec52a11eb85290242ac130003Checkout ID.
currencyString enum [ZAR, USD, KES, MUR, GBP, EUR]ZARThe currency code of the payment request amount.
merchant.nameStringPeach PaymentsThe merchant name.
merchantTransactionIdString (8-16)OrderNo453432Merchant-provided reference number unique for your transactions.
paymentTypeString enum [DB, PA]DBThe payment type for the request.
result.codeString100.396.104A code representing the checkout state.
result.descriptionStringUncertain status - probably cancelled by userA friendly message.
signatureString (64)a668342244a9c77...32035ed06164f4Token to verify the integrity of the webhook, ensuring the request is coming from Checkout.
timestampString2023-07-04T08:10:05ZDate and time when Peach Payments sent the webhook.

Checkout cancelled

Peach Payments sends this webhook when a user cancels a checkout instance.

ParameterTypeExampleDescription
amountString (^[0-9]{1,8}(\\.[0-9]{2})?$)1010.22The amount of the payment request. The period is the decimal separator. M-PESA does not support decimal amounts, so Checkout automatically rounds them up.
checkoutIdString (64)948cc8dec52a11eb85290242ac130003Checkout ID.
currencyString enum [ZAR, USD, KES, MUR, GBP, EUR]ZARThe currency code of the payment request amount.
merchant.nameStringPeach PaymentsThe merchant name.
merchantTransactionIdString (8-16)OrderNo453432Merchant-provided reference number unique for your transactions.
paymentTypeString enum [DB, PA]DBThe payment type for the request.
result.codeString100.396.101A code representing the checkout state.
result.descriptionStringCancelled by userA friendly message.
signatureString (64)a668342244a9c77...32035ed06164f4Token to verify the integrity of the webhook, ensuring the request is coming from Checkout.
timestampString2023-07-04T08:10:05ZDate and time when Peach Payments sent the webhook.

Example webhooks

amount=10.00&checkoutId=f4e5753843ea4851aec6ec7e3985a8az&currency=ZAR&merchant.name=Peach+Payments&merchantTransactionId=webhooktest01&paymentType=DB&result_code=000.200.100&result_description=successfully+created+checkout&signature=7b9112e285d00772eb898e3e7cd194b03fa87c85df53f06c81ba5b0b65c36abz&timestamp=2023-09-27T20%3A29%3A04Z
amount=10.00&checkoutId=f4e5753843ea4851aec6ec7e3985a8az&currency=ZAR&id=6b30c8d59dfc435896b925350fe69dbz&merchant.name=Peach+Payments&merchantTransactionId=webhooktest01&paymentBrand=CAPITECPAY&paymentType=DB&result_code=000.200.000&result_description=transaction+pending&signature=e16c91688e90abdae1b212264e16ac6d03dfb4e08342c67bcc1d09f3d3e8b63z&timestamp=2023-09-27T20%3A30%3A44Z
amount=10.00&card.bin=420000&card.expiryMonth=06&card.expiryYear=2056&card.holder=Test&card.last4Digits=0042&cardholderInitiatedTransactionId=123456789012345&checkoutId=89013492df434834b5cae990d6ed72ca&currency=ZAR&id=8ac7a4a09a9e2437019aa1af847c6524&merchant.name=Peach&merchantTransactionId=20251120163257&paymentBrand=VISA&paymentType=DB&recon.authCode=012345&recon.resultCode=00&recon.rrn=585101949257&recon.stan=957741&registrationId=8ac7a4a29a9e2455019aa1af83d47a98&result.code=000.100.110&result.description=Request+successfully+processed+in+%27Merchant+in+Integrator+Test+Mode%27&resultDetails.AcquirerResponse=00&resultDetails.ExtendedDescription=Approved+or+completed+successfully&signature=1c6e323ceda2452be88940e8fc01106a3928249b7169b498a937ee1644c45688&timestamp=2025-11-20T14%3A33%3A57Z
amount=10.00&bankAccount.bankCode=470010&bankAccount.bankName=capitec&billing.city=Cape+Town&billing.country=ZA&billing.postcode=8000&billing.street1=123+Main+Street&checkoutId=b361300e1b334acdb8bdd6e764ef0d9a&currency=ZAR&customer.email=test%40test.com&customer.givenName=John&customer.mobile=%2B27831234567&customer.surname=Doe&id=749a0cc6f45e4449b4bcb0c6fa3a0084&merchant.name=Peach+Payments&merchantTransactionId=20241106121719&paymentBrand=CAPITECPAY&paymentType=DB&result.code=000.100.110&result.description=Request+successfully+processed+in+%27Merchant+in+Integrator+Test+Mode%27&resultDetails.AcquirerResponse=SUCCESS&resultDetails.ConnectorTxID1=8cc4fc8a-9c28-11ef-89cc-0e61cffa7e17&resultDetails.ExtendedDescription=The+transaction+has+completed.&shipping.city=Cape+Town&shipping.country=ZA&shipping.postcode=8000&shipping.street1=123+Main+Street&signature=c63e56a78ede97d613e0287d8b1825f8c932bd64c554f2d20a997d366aca9626&timestamp=2024-11-06T10%3A19%3A39Z
amount=839.44&card.bin=&card.expiryMonth=&card.expiryYear=&card.last4Digits=&checkoutId=9f77244f9e2c4976996c6563f647b3f9&currency=ZAR&descriptor=&id=e9397e8649334ceaa765b2569b7f62ca&merchant.name=Peach-Testing&merchantTransactionId=reYsGSx77g7q21jVy9V4ESSAX&paymentBrand=1FORYOU&paymentType=RF&recon.authCode=None&recon.resultCode=None&recon.rrn=None&recon.stan=None&referencedId=6419b4099b7448139e7f129dcf6df755&result.code=000.100.110&result.description=Request+successfully+processed+in+%27Merchant+in+Integrator+Test+Mode%27&signature=b2d7c36a1b0efd1f6a08175ed3ea5c81c2325b2bcee88213d19a8bd735e17572&timestamp=2025-04-15T09%3A22%3A35Z
amount=10.00&checkoutId=fd5771f695924af6ade5fa5162789a2z&currency=ZAR&merchant.name=Peach+Payments&merchantTransactionId=webhooktest01&paymentType=DB&result_code=100.396.104&result_description=Uncertain+status+-+probably+cancelled+by+user&signature=f0e2fa5bc2b4c6255a6351df5614c35a9c01b9b5a3087956f873bc91115b5d6z&timestamp=2023-09-28T10%3A27%3A42Z
amount=10.00&checkoutId=ea266cc84b22402aad42e5d5f2995c7z&currency=ZAR&merchant.name=Peach+Payments&merchantTransactionId=webhooktest01&paymentType=DB&result_code=100.396.101&result_description=Cancelled+by+user&signature=353052add9889da0b8c6c15b8caf9ca71e4e736343183030307158595a98bf8z&timestamp=2023-09-27T20%3A46%3A56Z

Did this page help you?