Embedded Integration Code Examples

Build your own checkout using DigetPay's Server-to-Server (S2S) APIs.

Embedded Integration allows you to collect payment details on your own checkout page, then securely send the transaction from your backend to DigetPay.

🚧

PCI DSS Requirement

Embedded Integration requires secure handling of cardholder data (SAQ D or equivalent).

If you don't require full checkout customization, we strongly recommend using Hosted Checkout instead.

❗️

Never send card data directly from the browser

Card details should always be submitted to your backend, then your backend calls DigetPay APIs.

📘

Prerequisites

Before starting, make sure you have:

  • Sandbox account
  • Fin API Key
  • Merchant credentials
  • Backend server
  • HTTPS enabled
  • Knowledge of REST APIs

Payment Flow

sequenceDiagram

participant Customer
participant Merchant Backend
participant DigetPay

Customer->>Merchant Backend: Submit Card Details

Merchant Backend->>Merchant Backend: Generate MD5 Hash

Merchant Backend->>DigetPay: POST /payment/s2s/sale

DigetPay-->>Merchant Backend: Approved / Declined / 3DS HTML

Merchant Backend->>Customer: Show 3DS or Success Page

DigetPay->>Merchant Backend: Webhook Notification

Configuration

<?php
$config = [
    'environment' => getenv('DIGETPAY_ENV') ?: 'staging',
    'api_key' => getenv('DIGETPAY_API_KEY') ?: '',
    'environments' => [
        'staging' => ['api_base' => 'https://fin-api.digetpay.com/v1'],
        'production' => ['api_base' => 'https://api.digetpay.com/v1'],
    ],
];

</Tab>

<Tab title="Node.js">

```javascript
// config/digetpay.js
const env = process.env.DIGETPAY_ENV || 'staging';
const API_BASES = {
  staging: 'https://fin-api.digetpay.com/v1',
  production: 'https://api.digetpay.com/v1',
};
export const config = {
  environment: env,
  apiKey: process.env.DIGETPAY_API_KEY || '',
  apiBase: API_BASES[env],
};
if (!config.apiKey) throw new Error('DIGETPAY_API_KEY is not set');

Generate MD5 Request Hash

Every Embedded transaction requires generating a request hash.

Formula:

reverse(email)
+
API_KEY
+
reverse(first6 + last4 of card)

Uppercase the string then calculate the MD5 digest.

<?php
declare(strict_types=1);

function digetpayEmbeddedHash(string $email, string $cardNumber, string $apiKey): string
{
    $card = preg_replace('/\s+/', '', $cardNumber);
    $reversedEmail = strrev($email);
    $reversedCard = strrev(substr($card, 0, 6) . substr($card, -4));
    $raw = strtoupper($reversedEmail . $apiKey . $reversedCard);
    return md5($raw);
}

Full explanation: Request Hash (MD5).

Embedded Client

The Embedded client handles communication between your backend and the DigetPay API.

It provides helper methods for:

- Generate request hash
- Direct Sale
- Capture
- Void
- Transaction Status

<?php
declare(strict_types=1);

require_once __DIR__ . '/digetpay-hash.php';

final class DigetPayEmbeddedClient
{
    private string $apiBase;
    private string $apiKey;

    public function __construct(array $config)
    {
        $env = $config['environment'] ?? 'staging';
        $this->apiBase = rtrim($config['environments'][$env]['api_base'], '/');
        $this->apiKey = $config['api_key'] ?? '';
    }

    /** @return array<string, mixed> */
    public function directSale(array $order, array $card, array $customer, string $successUrl, string $failureUrl): array
    {
        $hash = digetpayEmbeddedHash($customer['email'], $card['cardNumber'], $this->apiKey);

        $body = [
            'orderId' => $order['orderId'],
            'amount' => $order['amount'],
            'currency' => $order['currency'] ?? 'SAR',
            'paymentMethod' => 'card',
            'auth' => $order['auth'] ?? 'N',
            'card' => [
                'cardNumber' => preg_replace('/\s+/', '', $card['cardNumber']),
                'cardExpiryMonth' => $card['cardExpiryMonth'],
                'cardExpiryYear' => $card['cardExpiryYear'],
                'cardCvv' => $card['cardCvv'],
                'cardHolder' => $card['cardHolder'],
            ],
            'customer' => $customer,
            'successUrl' => $successUrl,
            'failureUrl' => $failureUrl,
            'hash' => $hash,
        ];

        return $this->request('POST', '/payment/s2s/sale', $body);
    }

    /** @return array<string, mixed> */
    public function capture(string $transactionId, float $amount): array
    {
        return $this->request('POST', '/payment/s2s/capture', [
            'transactionId' => $transactionId,
            'amount' => $amount,
        ]);
    }

    /** @return array<string, mixed> */
    public function voidTransaction(string $transactionId): array
    {
        return $this->request('POST', '/payment/s2s/void', [
            'transactionId' => $transactionId,
        ]);
    }

    /** @return array<string, mixed> */
    public function getStatus(string $transactionId): array
    {
        return $this->request('POST', '/payment/s2s/status/' . urlencode($transactionId));
    }

    /** @return array<string, mixed> */
    private function request(string $method, string $path, ?array $body = null): array
    {
        $ch = curl_init($this->apiBase . $path);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_CUSTOMREQUEST => $method,
            CURLOPT_HTTPHEADER => [
                'Content-Type: application/json',
                'x-api-key: ' . $this->apiKey,
            ],
            CURLOPT_TIMEOUT => 60,
        ]);
        if ($body !== null) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body, JSON_THROW_ON_ERROR));
        }
        $raw = curl_exec($ch);
        $code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        if ($raw === false || $code >= 400) {
            throw new RuntimeException("Embedded HTTP $code: $raw");
        }
        return json_decode($raw, true) ?: [];
    }
}

Direct Sale Request

The Direct Sale API charges a payment card directly from your backend.

Endpoint

POST /payment/s2s/sale

Required information:

FieldDescription
orderIdMerchant order ID
amountTransaction amount
currencyCurrency code
cardCard information
customerCustomer information
successUrl3DS success return URL
failureUrlFailed payment URL
hashMD5 request hash

Process Payment

Receive card information from your checkout page and call the Direct Sale API.

<?php
declare(strict_types=1);

require_once __DIR__ . '/../src/DigetPayEmbeddedClient.php';

$config = require __DIR__ . '/../config/digetpay.php';
$client = new DigetPayEmbeddedClient($config);

try {
    $result = $client->directSale(
        order: [
            'orderId' => $_POST['order_id'],
            'amount' => (float) $_POST['amount'],
            'currency' => 'SAR',
            'auth' => 'N',
        ],
        card: [
            'cardNumber' => $_POST['card_number'],
            'cardExpiryMonth' => $_POST['exp_month'],
            'cardExpiryYear' => $_POST['exp_year'],
            'cardCvv' => $_POST['cvv'],
            'cardHolder' => $_POST['card_holder'],
        ],
        customer: [
            'name' => $_POST['card_holder'],
            'email' => $_POST['email'],
            'phone' => $_POST['phone'],
        ],
        successUrl: 'https://yourstore.com/payment/3ds-return',
        failureUrl: 'https://yourstore.com/payment/failed'
    );

    $data = $result['data'] ?? $result;
    $html = $data['html'] ?? $data['htmlContent'] ?? null;

    if ($html) {
        echo $html;
        exit;
    }

    if (($data['status'] ?? '') === 'APPROVED') {
        header('Location: /order/complete?paymentId=' . urlencode($data['paymentId'] ?? ''));
        exit;
    }

    header('Location: /payment/failed');
} catch (Throwable $e) {
    error_log($e->getMessage());
    http_response_code(500);
    echo 'Payment failed';
}

Handling the Response

The Direct Sale response can return one of three outcomes.

ResponseAction
ApprovedComplete the order
DeclinedShow payment failure
3-D Secure HTMLRender the HTML and allow the customer to authenticate

Example:

Approved
        ↓
Order Completed

Declined
        ↓
Payment Failed

3DS HTML
        ↓
Render HTML
        ↓
Customer Authentication
        ↓
Final Status

🚧

Best Practice

Do not mark an order as paid immediately after receiving an Approved response.

Always verify the final transaction using either:

  • Transaction Status API
  • Webhook Notification

Post-Authorization Operations

After processing an Embedded payment, you can perform additional operations such as:

- Capture an authorized transaction
- Void an authorized transaction
- Check transaction status
- Receive webhook notifications


Capture an Authorized Transaction

Use the Capture API to settle a previously authorized payment.

POST /payment/s2s/capture

Required fields:

FieldDescription
transactionIdDigetPay transaction ID
amountAmount to capture
// After auth-only sale (auth: Y)
$client->capture($transactionId, 10.00);



);

Void an Authorized Transaction

Use the Void API to cancel an authorized transaction before settlement.

POST /payment/s2s/void
$client->voidTransaction($transactionId);

Capture & Void guide →


Check Transaction Status

Retrieve the latest transaction status from DigetPay.

POST /payment/s2s/status/{transactionId}
$status = $client->getStatus($transactionId);

Transaction Status Values

StatusMeaning
APPROVEDPayment completed successfully
DECLINEDPayment rejected
PENDINGWaiting for completion
AUTHORIZEDAuthorized but not captured
CAPTUREDFunds captured
VOIDEDAuthorization cancelled

Receive Webhook Notifications

DigetPay sends asynchronous notifications whenever a payment status changes.

Customer Pays
        │
        ▼
DigetPay
        │
        ▼
POST Webhook
        │
        ▼
Your Backend
        │
        ▼
Update Order Status

Webhook URL example

POST /webhooks/digetpay

Event mapping: Webhook Events.
Webhook handler — see Webhooks Code.


🚧

Webhook Best Practices

  • Always return HTTP 200 immediately.
  • Process webhook events asynchronously.
  • Deduplicate events using transactionId.
  • Never rely only on browser redirects for payment confirmation.
📘

Recommended Flow

For the most reliable payment confirmation:

  1. Customer completes payment.
  2. Receive the webhook.
  3. Verify the transaction using the Status API.
  4. Update your order.
  5. Notify the customer.

Security Best Practices

Building your own checkout gives you full control over the payment experience, but it also means your application is responsible for securely handling sensitive payment information.

Follow these recommendations for every Embedded integration.

❗️

Never Log Card Data

Do not store or log:

  • Card Number (PAN)
  • CVV
  • Full expiration date
  • Request hash
  • Raw API requests containing card details
🚧

Always Use HTTPS

All customer checkout pages, backend APIs, and callback URLs must use HTTPS.

Never accept card details over an unencrypted connection.

📘

Verify Every Payment

Never trust:

  • Browser redirects
  • Client-side success messages

Always verify payment using either:

  • Transaction Status API
  • Webhook Notification

before marking an order as paid.


Complete Embedded Flow

flowchart TD

A[Customer enters card details]

B[Merchant Backend]

C[Generate MD5 Hash]

D[POST /payment/s2s/sale]

E{Response}

F[Approved]

G[3DS HTML]

H[Declined]

I[Customer completes 3DS]

J[Webhook]

K[Transaction Status API]

L[Update Order]

M[Capture or Void if Authorized]

A --> B
B --> C
C --> D
D --> E

E --> F
E --> G
E --> H

G --> I
I --> J

F --> J

J --> K
K --> L

L --> M

Troubleshooting

Invalid Hash

- Verify the MD5 algorithm.
- Reverse the email correctly.
- Use only the first 6 and last 4 card digits.
- Convert the input string to uppercase before hashing.


Transaction Declined

Possible causes include:

- Incorrect card details
- Insufficient funds
- Issuer rejection
- Fraud checks

Use the Transaction Status API for the final payment state.


3-D Secure Not Displayed

Verify that:

- successUrl is valid.
- failureUrl is valid.
- Your application renders the returned HTML without modification.


Duplicate Orders

Always use:

- Webhook deduplication
- transactionId
- Unique orderId

Never process the same payment twice.




Did this page help you?