Direct Pay API

Skip the hosted product-selection page. Your server posts product_id, quantity, and payment_gateway together with the usual buyer fields. The returned redirect URL opens the selected payment gateway immediately.

Credentials

Public
API Key
Same credentials as the hosted checkout API.
Secret
API Secret
HMAC-SHA256 signing key for Direct Session and webhooks. Never expose client-side.
Public
Store ID
Numeric store identifier.
Public
Product ID
Approved product belonging to your store.

Integration Flow

  • 1 Create direct session — POST buyer data + product_id + quantity + payment_gateway + HMAC hash to /checkout/token_direct.php.
  • 2 Redirect user — Forward the browser to the returned redirect URL. It auto-starts the chosen gateway payment page.
  • 3 Receive webhook — Same IPN format as hosted checkout. Respond with OK.
Server-side only. Never generate the hash or expose api_secret in front-end JavaScript or mobile apps.

vs Hosted Checkout

Use Direct Pay when you already know the product, quantity, and payment method. Use hosted checkout (v1) when the customer should pick those on the Dodopin store page.

Endpoint: /checkout/token_direct.php (v2) vs /checkout/token.php (v1). Hash string for v2 includes product, quantity, and gateway.

Authentication

Sign every Direct Session request with HMAC-SHA256. The hash string is longer than v1 — it includes product and gateway fields.

Request Hash (Direct Session)

  hash_string =
 api_key
+ "|" +store_id
+ "|" +user_id
+ "|" +username
+ "|" +user_email
+ "|" +product_id
+ "|" +quantity
+ "|" +payment_gateway
hash = base64( HMAC-SHA256( hash_string, api_secret ) )

PHP

$hash_string =
    $API_KEY . '|' .
    $store_id . '|' .
    $user_id . '|' .
    $username . '|' .
    $user_email . '|' .
    $product_id . '|' .
    $quantity . '|' .
    $payment_gateway;

$hash = base64_encode(
    hash_hmac('sha256', $hash_string, $API_SECRET, true)
);

Create Direct Session

POST credentials, buyer context, product, quantity, and gateway. On success you receive a signed redirect URL that opens payment immediately.

POST https://dodopin.com/checkout/token_direct.php Content-Type: form-urlencoded → JSON

Required fields

FieldDescription
api_keyStore API key
hashHMAC signature (see Authentication)
store_idNumeric store ID
user_idYour end-user identifier
usernameLetters, digits, _, - only
user_emailValid email
user_ipBuyer IPv4/IPv6
user_fullnameBuyer full name
user_phoneE.g. +13125550100
product_idApproved product ID for this store
quantity1–99999 (1 if product disallows quantity)
payment_gatewayGateway key (see Payment Gateways)

Optional

FieldDescription
langUI language. Default: tr. Allowed: tr, en, de, fr, es, it, pt, nl, pl, ro, cs
currencyDisplay currency, e.g. TRY

Code Examples

<?php
$API_KEY    = 'YOUR_API_KEY';
$API_SECRET = 'YOUR_API_SECRET';
$store_id   = 12345;
$user_id    = 678;
$username   = 'username';
$user_email = '[email protected]';
$product_id = 104;
$quantity   = 1;
$payment_gateway = 'lidio';

$hash_string = $API_KEY . '|' . $store_id . '|' . $user_id . '|' . $username . '|' . $user_email
    . '|' . $product_id . '|' . $quantity . '|' . $payment_gateway;
$hash = base64_encode(hash_hmac('sha256', $hash_string, $API_SECRET, true));

$post_data = [
    'api_key'          => $API_KEY,
    'hash'             => $hash,
    'store_id'         => $store_id,
    'user_id'          => $user_id,
    'username'         => $username,
    'user_email'       => $user_email,
    'user_ip'          => $_SERVER['REMOTE_ADDR'] ?? '',
    'user_fullname'    => 'John Doe',
    'user_phone'       => '+13125550100',
    'product_id'       => $product_id,
    'quantity'         => $quantity,
    'payment_gateway'  => $payment_gateway,
    'lang'             => 'tr',
    'currency'         => 'TRY',
];

$ch = curl_init('https://dodopin.com/checkout/token_direct.php');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => $post_data,
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_TIMEOUT        => 30,
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);

if (!empty($response['success'])) {
    header('Location: ' . $response['redirect']);
    exit;
}
echo $response['message'] ?? 'error';
const crypto = require('crypto');
const API_KEY = 'YOUR_API_KEY';
const API_SECRET = 'YOUR_API_SECRET';
const store_id = 12345;
const user_id = 678;
const username = 'username';
const user_email = '[email protected]';
const product_id = 104;
const quantity = 1;
const payment_gateway = 'lidio';

const hashString = [API_KEY, store_id, user_id, username, user_email, product_id, quantity, payment_gateway].join('|');
const hash = Buffer.from(
  crypto.createHmac('sha256', API_SECRET).update(hashString).digest()
).toString('base64');

const body = new URLSearchParams({
  api_key: API_KEY,
  hash,
  store_id: String(store_id),
  user_id: String(user_id),
  username,
  user_email,
  user_ip: '203.0.113.10',
  user_fullname: 'John Doe',
  user_phone: '+13125550100',
  product_id: String(product_id),
  quantity: String(quantity),
  payment_gateway,
  lang: 'tr',
  currency: 'TRY',
});

const res = await fetch('https://dodopin.com/checkout/token_direct.php', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body,
});
const data = await res.json();
// redirect user to data.redirect
import base64, hashlib, hmac, urllib.parse, urllib.request

API_KEY = 'YOUR_API_KEY'
API_SECRET = 'YOUR_API_SECRET'
store_id = 12345
user_id = 678
username = 'username'
user_email = '[email protected]'
product_id = 104
quantity = 1
payment_gateway = 'lidio'

hash_string = '|'.join(map(str, [
    API_KEY, store_id, user_id, username, user_email, product_id, quantity, payment_gateway
]))
digest = hmac.new(API_SECRET.encode(), hash_string.encode(), hashlib.sha256).digest()
hash_b64 = base64.b64encode(digest).decode()

post = urllib.parse.urlencode({
    'api_key': API_KEY,
    'hash': hash_b64,
    'store_id': store_id,
    'user_id': user_id,
    'username': username,
    'user_email': user_email,
    'user_ip': '203.0.113.10',
    'user_fullname': 'John Doe',
    'user_phone': '+13125550100',
    'product_id': product_id,
    'quantity': quantity,
    'payment_gateway': payment_gateway,
    'lang': 'tr',
    'currency': 'TRY',
}).encode()

req = urllib.request.Request(
    'https://dodopin.com/checkout/token_direct.php',
    data=post,
    method='POST',
)
with urllib.request.urlopen(req) as resp:
    print(resp.read().decode())
# Compute hash server-side first, then:
curl -X POST 'https://dodopin.com/checkout/token_direct.php' \
  -d 'api_key=YOUR_API_KEY' \
  -d 'hash=BASE64_HMAC' \
  -d 'store_id=12345' \
  -d 'user_id=678' \
  -d 'username=username' \
  -d '[email protected]' \
  -d 'user_ip=203.0.113.10' \
  -d 'user_fullname=John+Doe' \
  -d 'user_phone=%2B13125550100' \
  -d 'product_id=104' \
  -d 'quantity=1' \
  -d 'payment_gateway=lidio' \
  -d 'lang=tr' \
  -d 'currency=TRY'

Success response

{
  "success": true,
  "token": "…",
  "redirect": "https://…/checkout/direct_pay.php?token=…&product_id=104&quantity=1&payment_gateway=lidio&lang=tr&sig=…",
  "product_id": 104,
  "quantity": 1,
  "payment_gateway": "lidio"
}

Payment Gateways

Pass one of these keys as payment_gateway. The method must be enabled globally and not disabled for your store.

KeyType
lidioCard (Lidio)
tikoCard (Tiko)
tazapayCard (Tazapay)
stripe / stripe_cardStripe Cards
stripe_ideal, stripe_p24, stripe_apple_pay, …Stripe surfaces
bank_transferTurkey IBAN transfer
payop_fawry, payop_…Local PayOp methods
Stripe surface keys match checkout rows (e.g. stripe_google_pay). PayOp keys use the payop_ prefix of the method slug shown in admin.

Webhooks

Direct Pay uses the same webhook (IPN) format, signing rules, and retry policy as hosted checkout. See the v1 Webhooks section for field lists and verification examples. Max delivery attempts: 100.

Error Codes

Failures return JSON { "success": false, "message": "…" }. Common messages include invalid hash, missing fields, product not found, insufficient pin stock, or payment gateway unavailable for the store.

Rate limits apply (same as token.php). HTTP 429 means retry after a short wait.