Hosted Payment Page Integration Guide
A step-by-step guide to integrate the Cycle Hosted Payment Page into your website or application.
Overview
The Cycle Hosted Payment Page is a secure, PCI DSS-compliant payment page that supports a wide range of payment methods across multiple markets. The integration follows a simple flow.
Build a JSON Payload
Sign the Payload
POST to the Hosted Payment Page URL
Embed the Hosted Payment Page or redirect
Listen for PostMessage Events for UI updates
Handle Server Notifications to confirm charges
1. Build the Payload
Create a JSON object with the required merchant, customer, and charge details. Here’s a minimal example in PHP:
$payload = [
'merchantAccount' => 'YourMerchantAccount', // Required
'timestamp' => time(), // Required (Unix format)
'chargeId' => uniqid('txn_'), // Optional, but recommended
'price' => '100.00', // Required
'currency' => 'ZAR', // Required
'description' => 'Product Name or Description', // Required
'customerId' => 'user_123456', // Required
'customerLocale' => 'en_ZA', // Optional
'customerCountry' => 'ZA', // Optional
'mode' => 'DEEP_LINK', // Required
'paymentMethod' => 'CREDIT_CARD', // Required
'flowCompletionUrl' => 'https://yourdomain.com/thankyou', // Optional
'callbackUrl' => 'https://yourdomain.com/callback' // Optional (for real-time notifications)
];
Additional optional fields include customerEmail, memo, recurrence, savePaymentProfile, and various completion URLs (successUrl, failureUrl, cancelUrl).
2. Sign the Payload
Use SHA256 to sign the JSON payload using your unique paywallSecretKey.
$jsonPayload = json_encode($payload);
$signature = hash('sha256', $paywallSecretKey . $jsonPayload);
3. Submit to the Hosted Payment Page
Use a form POST to send the payload and signature to the Hosted Payment Page URL. You can target an iframe or perform a direct redirect.
<form id="paywallForm" action="https://sandbox.cyclepay.net/pwv4/launch" method="POST" target="paywallIframe">
<input type="hidden" name="payload" value='<?= $jsonPayload ?>'>
<input type="hidden" name="signature" value='<?= $signature ?>'>
</form>
<iframe name="paywallIframe" width="100%" height="650" frameborder="0"></iframe>
<script>
document.getElementById('paywallForm').submit();
</script>
4. Handle Events (PostMessage)
The Hosted Payment Page communicates with your parent page using window.postMessage for real-time UI updates. Common message types include initialized, submitted, success, and close.
window.addEventListener("message", function(event) {
if (event.origin !== "https://sandbox.cyclepay.net") return;
try {
const message = JSON.parse(event.data);
if (message.name === "PURCHASE_STATUS") {
if (message.success) {
alert("Payment Successful!");
} else if (message.close) {
alert("Paywall Closed.");
}
}
} catch (e) {
console.error("Invalid paywall message", e);
}
});
5. Handle Backend Notifications (Callback URL)
For fulfillment and reconciliation, handle the real-time server-to-server notifications. You must verify the X-callback-signature header using your notificationKey.
$raw = file_get_contents("php://input");
$headers = apache_request_headers();
$signature = hash_hmac('sha256', $raw, $notificationKey);
if (strcasecmp($signature, $headers['X-callback-signature']) === 0) {
$event = json_decode($raw, true);
// Process order fulfillment, logging, etc.
http_response_code(200);
} else {
http_response_code(403); // Invalid signature
}
Example Callback Payload
{
"id": "202110112305",
"internalId": "2347897968234687234",
"status": "CHARGED",
"merchantAccount": "YourMerchant",
"lineItem": {
"amount": 12.70,
"currency": "ZAR",
"description": "Product Description",
"taxCode": "NT",
"taxIncludedInPrice": true
},
"transactions": [
{
"timestamp": "2021-10-11T15:07:17Z",
"gatewayProvidedId": "dd317ae1...",
"type": "CHARGE",
"state": "APPROVED",
"amount": 12.70,
"currency": "ZAR",
"resultCode": 0,
"responseText": "Success"
}
],
"paymentType": "CREDIT_CARD",
"paymentMethod": "Visa",
"created": "2021-10-11T15:07:16Z",
"amountRemaining": 0.00
}
Always respond with a 200 OK status to acknowledge receipt of the callback. Otherwise, Cycle will retry the notification for up to 3 days.