# DOKU JS Integration Guide

### What is DOKU JS ?&#x20;

The **DOKU JS Integration** feature enables non-PCI DSS certified merchants to securely collect customer card details for online transactions by embedding a Javascript into their HTML files while still having the freedom to manages how their checkoutpage will looks like. **PCI DSS compliance is needed before activating this feature please contact DOKU team first.**

### Integration steps

Understanding the complete payment flow from merchant perspective:

{% hint style="info" %}
**Prerequisites:**

* **Create session\_id from your backend:** Call POST [/request-payment-page to get payment URL](https://developers.doku.com/accept-payments/direct-api/non-snap/cards/payment-page-integration-guide) and get session ID
* A valid DOKU payment session ID from your backend
* Basic HTML/JavaScript knowledge
* Web server or local development environment
  {% endhint %}

<figure><img src="https://3092822868-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FqCxtvLoJNNxvp4U7kLHd%2Fuploads%2FuZP4NBW78xB1OTVo02DJ%2Fflow%20JS.JPG?alt=media&#x26;token=e332b77c-779d-4dd6-82ce-dec4e63438fb" alt=""><figcaption><p>DOKU JS Transaction Flow</p></figcaption></figure>

#### Example HTML Files

```
<!DOCTYPE html>
<html>
<head>
    <title>DOKU Payment Integration</title>
    <!-- Step 1: Include the SDK -->
    <script src="https://sandbox.doku.com/card-session/card-session-1.0.0.js"></script>
</head>
<body>
    <form id="payment-form">
        <input type="text" id="sessionId" placeholder="Payment Session ID" required />
        <input type="text" id="cardNumber" placeholder="Card Number" required />
        <input type="text" id="expiryMonth" placeholder="MM" required />
        <input type="text" id="expiryYear" placeholder="YY" required />
        <input type="text" id="cvv" placeholder="CVV" required />
        <input type="text" id="firstName" placeholder="First Name" required />
        <input type="text" id="lastName" placeholder="Last Name" required />
        <button type="submit">Pay Now</button>
    </form>

    <script>
        // Step 2: Initialize SDK
        PG.init();

        // Step 3: Handle form submission
        document.getElementById('payment-form').addEventListener('submit', function(e) {
            e.preventDefault();

            const cardData = {
                session_id: document.getElementById('sessionId').value.trim(),
                card_number: document.getElementById('cardNumber').value.replace(/\s/g, ''),
                expiry_month: document.getElementById('expiryMonth').value.trim(),
                expiry_year: document.getElementById('expiryYear').value.trim(),
                cvv: document.getElementById('cvv').value.trim(),
                card_holder_first_name: document.getElementById('firstName').value.trim(),
                card_holder_last_name: document.getElementById('lastName').value.trim()
            };

            // Step 4: Submit payment
            PG.payment.collectCardData(cardData, function(error, response) {
                if (error) {
                    alert('Payment failed: ' + error.message);
                } else {
                    alert('Payment successful!');
                    console.log('Payment response:', response);
                }
            });
        });
    </script>
</body>
</html>
```

#### Step 1: Include the SDK

Add the SDK script to your HTML page according to your environment:

```
<!-- For sandbox/testing environment -->
<script src="https://sandbox.doku.com/card-session/card-session-1.0.4.js"></script>

<!-- For production environment -->
<script src="https://app.doku.com/card-session/card-session-1.0.4.js"></script>
```

{% hint style="warning" %}
**⚠️ Important:**

1. Each SDK URL is environment-specific. Always use sandbox URL for testing and production URL for live payments. Never use sandbox SDK in production!
2. HTML ID must be the same as stated in this documentation
3. **Note:** Place this in your \<head> section or before the closing \</body> tag.
   {% endhint %}

#### Step 2: Create Your Payment Form

Create a form with the required input fields:

```
<form id="credit-card-form">
    <!-- Session ID (obtained from your backend) -->
    <div class="form-group">
        <label for="paymentSessionId">Session ID</label>
        <input type="text" id="paymentSessionId" name="payment-session-id" required />
    </div>

    <!-- Card Information -->
    <div class="form-group">
        <label for="cardNumber">Card Number</label>
        <input type="text" id="cardNumber" name="card-number" required />
    </div>

    <div class="form-group">
        <label for="expiryMonth">Expiry Month (MM)</label>
        <input type="text" id="expiryMonth" name="expiry-month" placeholder="12" required />
    </div>

    <div class="form-group">
        <label for="expiryYear">Expiry Year (YY)</label>
        <input type="text" id="expiryYear" name="expiry-year" placeholder="27" required />
    </div>

    <div class="form-group">
        <label for="cvv">CVV</label>
        <input type="text" id="cvv" name="cvv" required />
    </div>

    <!-- Cardholder Information -->
    <div class="form-group">
        <label for="firstName">First Name</label>
        <input type="text" id="firstName" name="first-name" required />
    </div>

    <div class="form-group">
        <label for="lastName">Last Name</label>
        <input type="text" id="lastName" name="last-name" required />
    </div>
</form>
```

{% hint style="info" %}
ID must be the same as stated in this documentation
{% endhint %}

#### Step 3: Initialize the SDK

Add this script to initialize the SDK:

```
// Initialize the Payment Gateway SDK
PG.init();

```

Place this after the SDK script is loaded, preferably in a tag at the bottom of your page.

#### Step 4: Handle Form Submission

Capture the form submission and send the payment data to DOKU:

```
const form = document.getElementById('credit-card-form');

form.addEventListener('submit', function(e) {
    e.preventDefault();
    submitPayment();
});

function submitPayment() {
    // Collect card data from form
    const cardData = {
        session_id: document.getElementById('paymentSessionId').value.trim(),
        card_number: document.getElementById('cardNumber').value.replace(/\s/g, ''),
        expiry_month: document.getElementById('expiryMonth').value.trim(),
        expiry_year: document.getElementById('expiryYear').value.trim(),
        cvv: document.getElementById('cvv').value.trim(),
        card_holder_first_name: document.getElementById('firstName').value.trim(),
        card_holder_last_name: document.getElementById('lastName').value.trim(),
    };

    // Submit to DOKU
    PG.payment.collectCardData(cardData, function(error, response) {
        console.log('Payment response:', response);
        console.log('Payment error:', error);

        if (error) {
            // Handle error
            alert('Payment failed: ' + error.message);
            // You can show error message to user or redirect to error page
        } else {
            // Handle success
            alert('Payment successful!');
            // You can redirect to success page or show confirmation
            // window.location.href = '/payment-success';
        }
    });
}
```

### API References

#### PG.init()

Initialize the Payment Gateway SDK. Call this once when the page loads.

```
PG.init();
```

**Parameters:** None

**Returns:** void

#### PG.payment.collectCardData(cardData, callback)

Submit card data for payment processing.

```
PG.payment.collectCardData(cardData, callback);
```

**Required Parameters (cardData object):**

| Field                                                                            | Type   | Description                     | Example                                  |
| -------------------------------------------------------------------------------- | ------ | ------------------------------- | ---------------------------------------- |
| `session_id` <mark style="background-color:red;">**REQUIRED**</mark>             | string | Payment session ID from backend | "ps\_sandbox\_1762256259630\_lRDnbpJT3c" |
| `card_number` <mark style="background-color:red;">**REQUIRED**</mark>            | string | Card number (no spaces)         | "4512490000000907"                       |
| `expiry_month` <mark style="background-color:red;">**REQUIRED**</mark>           | string | Card expiry month (MM)          | "12"                                     |
| `expiry_year` <mark style="background-color:red;">**REQUIRED**</mark>            | string | Card expiry year (YY or YYYY)   | "27" or "2027"                           |
| `cvv` <mark style="background-color:red;">**REQUIRED**</mark>                    | string | Card CVV/CVV2 code              | "123"                                    |
| `card_holder_first_name` <mark style="background-color:red;">**REQUIRED**</mark> | string | Cardholder first name           | "John"                                   |
| `card_holder_last_name` <mark style="background-color:red;">**REQUIRED**</mark>  | string | Cardholder last name            | "Doe"                                    |

#### **Callback Function:**

```
function callback(error, response) {
    if (error) {
        // Error handling
        console.error(error.message);
    } else {
        // Success handling
        console.log(response);
    }
}
```

**Callback Parameters:**

> * `error`: Error object if payment fails (null on success)
>   * `error.message`: Error message string
> * `response`: Response object if payment succeeds (null on error)
>   * Contains payment result data

### Troubleshooting

#### Common Issues

<details>

<summary><strong>1. SDK Not Loaded</strong></summary>

**Error:** `PG is not defined`

**Solution:** Make sure the SDK script is loaded before your code:

```
<script src="https://sandbox.doku.com/card-session/card-session-1.0.0.js"></script>
<script>
    // Your code here
    PG.init();
</script>
```

</details>

<details>

<summary><strong>2. Invalid Session ID</strong></summary>

**Error:** `Invalid session ID`

**Solution:** Ensure you're getting a valid payment session ID from your backend API.

</details>

<details>

<summary><strong>3. Card Validation Errors</strong></summary>

**Error:** `Invalid card number`

**Solution:**

* Remove all spaces from card number
* Verify the card number is valid
* Use test cards in sandbox environment

</details>

<details>

<summary><strong>4. Network Errors</strong></summary>

**Error:** `Network request failed`

**Solution:**

* Check your internet connection
* Verify the SDK URL is correct for your environment
* Check browser console for CORS errors

</details>

<details>

<summary><strong>5. Form Not Submitting</strong></summary>

**Issue:** Clicking submit button does nothing

**Solution:**

* Ensure `e.preventDefault()` is called in the submit handler
* Check browser console for JavaScript errors
* Verify all required fields have values

</details>

#### Debug Mode

Enable console logging to debug issues:

```
PG.payment.collectCardData(cardData, function(error, response) {
    console.log('Card Data:', cardData);
    console.log('Error:', error);
    console.log('Response:', response);

    // Your handling code
});
```

### Security Best Practices

#### 1. Never Store Card Data

Never save card numbers, CVV, or sensitive data in your database or logs. The SDK handles secure transmission of card data to DOKU's servers.

#### 2. Use HTTPS

Always serve your payment page over HTTPS to ensure encrypted communication between the user's browser and your server.

#### 3. Validate Input

Validate user input on both client and server side to prevent malicious data submission and improve user experience.

#### 4. Session Management

Generate payment session IDs server-side with proper authentication. Never expose your API credentials on the client side.

#### 5. Anti-Clickjacking Protection

**What is Clickjacking?** Clickjacking is an attack where a malicious actor tricks users into clicking on hidden elements by overlaying transparent layers on your payment page.

Protect your payment integration by implementing these defense mechanisms:

**JavaScript Frame-Breaker**

Add this script to prevent your payment page from being loaded in unauthorized iframes:

```
<script type="text/javascript">
  // Anti-clickjacking frame-breaker
  if (self === top) {
    // Page is not in an iframe - safe to proceed
    var antiClickjack = document.getElementById("antiClickjack");
    if (antiClickjack) {
      antiClickjack.parentNode.removeChild(antiClickjack);
    }
  } else {
    // Page is in an iframe - redirect to legitimate location
    top.location = self.location;
  }
</script>
```

Add this style in your page `<head>` to hide content until the frame-breaker runs:

```
<style id="antiClickjack">
  body { display: none !important; }
</style>
```

**X-Frame-Options HTTP Header**

Configure your web server to send the X-Frame-Options header to prevent framing:

**Apache (.htaccess or httpd.conf):**

```
Header always set X-Frame-Options "DENY"
# Or to allow same-origin framing only:
# Header always set X-Frame-Options "SAMEORIGIN"
```

**Nginx:**

```
add_header X-Frame-Options "DENY" always;
# Or to allow same-origin framing only:
# add_header X-Frame-Options "SAMEORIGIN" always;
```

**Node.js/Express:**

```
app.use((req, res, next) => {
  res.setHeader('X-Frame-Options', 'DENY');
  next();
});
```

**Content-Security-Policy Header**

Implement CSP with `frame-ancestors` directive for more granular control:

**Apache:**

```
Header always set Content-Security-Policy "frame-ancestors 'none'"
# Or to allow specific domains:
# Header always set Content-Security-Policy "frame-ancestors 'self' https://trusted-domain.com"
```

**Nginx:**

```
add_header Content-Security-Policy "frame-ancestors 'none'" always;
# Or to allow specific domains:
# add_header Content-Security-Policy "frame-ancestors 'self' https://trusted-domain.com" always;
```

**Node.js/Express:**

```
app.use((req, res, next) => {
  res.setHeader('Content-Security-Policy', "frame-ancestors 'none'");
  next();
});
```

**Best Practice: Defense in Depth**

**Implement multiple layers of clickjacking protection for maximum security:**

* JavaScript frame-breaker (client-side)
* X-Frame-Options header (HTTP-level)
* Content-Security-Policy header (modern standard)

**Reference:** [OWASP Clickjacking Defense Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Clickjacking_Defense_Cheat_Sheet.html)

#### 6. PCI Compliance

The SDK handles card data securely, but ensure your implementation follows PCI DSS guidelines. This includes:

* Never logging sensitive card data
* Implementing proper access controls
* Regular security assessments
* Secure coding practices

### Next Steps

1. Test the integration in sandbox environment
2. Implement proper error handling
3. Add form validation
4. Style your payment form
5. Set up production environment
6. Implement server-side payment verification
