Cycle Logo

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.