This guide demonstrates how to integrate and use the Doku Snap SDK for various payment operations in your PHP project.
- Installation
- Configuration
- Usage
- Error Handling
- Advanced Usage
- Best Practices for Controllers
To install the Doku Snap SDK, use Composer:
composer require doku/doku-php-library
Before using the Doku Snap SDK, you need to initialize it with your credentials:
use Doku\Snap\Snap;
$privateKey = "YOUR_PRIVATE_KEY";
$publicKey = "YOUR_PUBLIC_KEY";
$clientId = "YOUR_CLIENT_ID";
$secretKey = "YOUR_SECRET_KEY";
$isProduction = false; // Set to true for production environment
$issuer = "YOUR_ISSUER"; // Optional
$authCode = "YOUR_AUTH_CODE"; // Optional
$snap = new Snap($privateKey, $publicKey, $clientId, $issuer, $isProduction, $secretKey, $authCode);
Always start by initializing the Snap object:
$snap = new Snap($privateKey, $publicKey, $clientId, $issuer, $isProduction, $secretKey, $authCode);
$currentToken = $snap->getCurrentTokenB2B();
echo "Current Token: " . $currentToken . PHP_EOL;
$newTokenResponse = $snap->getB2BToken($privateKey, $clientId, $isProduction);
echo "New Token: " . $newTokenResponse->accessToken . PHP_EOL;
use Doku\Snap\Models\VA\Request\CreateVaRequestDto;
use Doku\Snap\Models\TotalAmount\TotalAmount;
use Doku\Snap\Models\VA\AdditionalInfo\CreateVaRequestAdditionalInfo;
use Doku\Snap\Models\VA\VirtualAccountConfig\CreateVaVirtualAccountConfig;
$createVaRequestDto = new CreateVaRequestDto(
"8129014", // partner
"17223992157", // customerno
"812901417223992157", // customerNo
"T_" . time(), // virtualAccountName
"test.example." . time() . "@test.com", // virtualAccountEmail
"621722399214895", // virtualAccountPhone
"INV_CIMB_" . time(), // trxId
new TotalAmount("12500.00", "IDR"), // totalAmount
new CreateVaRequestAdditionalInfo("VIRTUAL_ACCOUNT_BANK_CIMB", new CreateVaVirtualAccountConfig(true)), // additionalInfo
'C', // virtualAccountTrxType
"2024-08-31T09:54:04+07:00" // expiredDate
);
$result = $snap->createVa($createVaRequestDto);
echo json_encode($result, JSON_PRETTY_PRINT);
use Doku\Snap\Models\VA\Request\UpdateVaRequestDto;
use Doku\Snap\Models\VA\AdditionalInfo\UpdateVaRequestAdditionalInfo;
use Doku\Snap\Models\VA\VirtualAccountConfig\UpdateVaVirtualAccountConfig;
$updateVaRequestDto = new UpdateVaRequestDto(
"8129014", // partnerServiceId
"17223992155", // customerNo
"812901417223992155", // virtualAccountNo
"T_" . time(), // virtualAccountName
"test.example." . time() . "@test.com", // virtualAccountEmail
"00000062798", // virtualAccountPhone
"INV_CIMB_" . time(), // trxId
new TotalAmount("14000.00", "IDR"), // totalAmount
new UpdateVaRequestAdditionalInfo("VIRTUAL_ACCOUNT_BANK_CIMB", new UpdateVaVirtualAccountConfig("ACTIVE", "10000.00", "15000.00")), // additionalInfo
"O", // virtualAccountTrxType
"2024-08-02T15:54:04+07:00" // expiredDate
);
$result = $snap->updateVa($updateVaRequestDto);
echo json_encode($result, JSON_PRETTY_PRINT);
use Doku\Snap\Models\VA\Request\DeleteVaRequestDto;
use Doku\Snap\Models\VA\AdditionalInfo\DeleteVaRequestAdditionalInfo;
$deleteVaRequestDto = new DeleteVaRequestDto(
"8129014", // partnerServiceId
"17223992155", // customerNo
"812901417223992155", // virtualAccountNo
"INV_CIMB_" . time(), // trxId
new DeleteVaRequestAdditionalInfo("VIRTUAL_ACCOUNT_BANK_CIMB") // additionalInfo
);
$result = $snap->deletePaymentCode($deleteVaRequestDto);
echo json_encode($result, JSON_PRETTY_PRINT);
use Doku\Snap\Models\VA\Request\CheckStatusVaRequestDto;
$checkStatusVaRequestDto = new CheckStatusVaRequestDto(
"8129014", // partnerServiceId
"17223992155", // customerNo
"812901417223992155", // virtualAccountNo
null,
null,
null
);
$result = $snap->checkStatusVa($checkStatusVaRequestDto);
echo json_encode($result, JSON_PRETTY_PRINT);
use Doku\Snap\Models\AccountBinding\AccountBindingRequestDto;
use Doku\Snap\Models\AccountBinding\AccountBindingAdditionalInfoRequestDto;
$additionalInfo = new AccountBindingAdditionalInfoRequestDto(
"Mandiri", // channel
"CUST123", // custIdMerchant
"John Doe", // customerName
"john.doe@example.com", // email
"1234567890", // idCard
"Indonesia", // country
"123 Main St, Jakarta", // address
"19900101", // dateOfBirth
"https://success.example.com", // successRegistrationUrl
"https://fail.example.com", // failedRegistrationUrl
"iPhone 12", // deviceModel
"iOS", // osType
"CH001" // channelId
);
$accountBindingRequestDto = new AccountBindingRequestDto(
"6281234567890", // phoneNo
$additionalInfo
);
$result = $snap->doAccountBinding($accountBindingRequestDto, $privateKey, $clientId, $secretKey, $isProduction);
echo json_encode($result, JSON_PRETTY_PRINT);
use Doku\Snap\Models\AccountUnbinding\AccountUnbindingRequestDto;
use Doku\Snap\Models\AccountUnbinding\AccountUnbindingAdditionalInfoRequestDto;
$additionalInfo = new AccountUnbindingAdditionalInfoRequestDto("Mandiri");
$accountUnbindingRequestDto = new AccountUnbindingRequestDto(
"tokenB2b2c123", // tokenId (tokenB2b2c)
$additionalInfo
);
$result = $snap->doAccountUnbinding($accountUnbindingRequestDto, $privateKey, $clientId, $secretKey, $isProduction);
echo json_encode($result, JSON_PRETTY_PRINT);
use Doku\Snap\Models\PaymentJumpApp\PaymentJumpAppRequestDto;
use Doku\Snap\Models\PaymentJumpApp\PaymentJumpAppAdditionalInfoRequestDto;
use Doku\Snap\Models\PaymentJumpApp\UrlParamDto;
$timestamp = time();
$totalAmount = new TotalAmount("50000.00", "IDR");
$additionalInfo = new PaymentJumpAppAdditionalInfoRequestDto(
"Mandiri", // channel
"Payment for Order #123", // remarks
"merchantId"
);
$urlParam = new UrlParamDto("url", "type", "no");
$paymentJumpAppRequestDto = new PaymentJumpAppRequestDto(
"ORDER_" . $timestamp, // partnerReferenceNo
date('Y-m-d H:i:s', strtotime('+1 day')), // validUpTo (24 hours from now)
"12", // pointOfInitiation
$urlParam,
$totalAmount,
$additionalInfo
);
$deviceId = "DEVICE_ID_123";
$result = $snap->doPaymentJumpApp($paymentJumpAppRequestDto, $deviceId, $privateKey, $clientId, $secretKey, $isProduction);
echo json_encode($result, JSON_PRETTY_PRINT);
use Doku\Snap\Models\CardRegistration\CardRegistrationRequestDto;
use Doku\Snap\Models\CardRegistration\CardRegistrationAdditionalInfoRequestDto;
$additionalInfo = new CardRegistrationAdditionalInfoRequestDto(
'Mandiri',
'John Doe',
'john@example.com',
'1234567890',
'ID',
'123 Main St',
'19900101',
'http://success.url',
'http://failed.url'
);
$cardRegistrationRequestDto = new CardRegistrationRequestDto(
'encrypted_card_data',
'cust123',
'081234567890',
$additionalInfo
);
$deviceId = "DEVICE_ID_123";
$response = $snap->doCardRegistration(
$cardRegistrationRequestDto,
$deviceId,
$privateKey,
$clientId,
$secretKey,
$isProduction
);
echo json_encode($response, JSON_PRETTY_PRINT);
use Doku\Snap\Models\AccountUnbinding\AccountUnbindingRequestDto;
use Doku\Snap\Models\AccountUnbinding\AccountUnbindingAdditionalInfoRequestDto;
$additionalInfo = new AccountUnbindingAdditionalInfoRequestDto("Mandiri");
$cardUnbindingRequestDto = new AccountUnbindingRequestDto(
"tokenB2b2c123", // tokenId (tokenB2b2c)
$additionalInfo
);
$result = $snap->doCardUnbinding($cardUnbindingRequestDto, $privateKey, $clientId, $secretKey, $isProduction);
echo json_encode($result, JSON_PRETTY_PRINT);
use Doku\Snap\Models\CheckStatus\CheckStatusRequestDto;
use Doku\Snap\Models\CheckStatus\CheckStatusAdditionalInfoRequestDto;
$checkStatusRequestDto = new CheckStatusRequestDto(
"originalPartnerRefNo123", // originalPartnerReferenceNo
"originalRefNo456", // originalReferenceNo
"originalExtId789", // originalExternalId
"SERVICE_CODE_001", // serviceCode
"2023-08-29T12:00:00+07:00", // transactionDate
new TotalAmount(100000, "IDR"),// totalAmount
"MERCHANT_001", // merchantId
"SUBMERCHANT_001", // subMerchantId
"STORE_001", // externalStoreId
new CheckStatusAdditionalInfoRequestDto("DEVICE_001", "DIRECT_DEBIT_MANDIRI") // additionalInfo
);
$authCode = "exampleAuthCode456";
$response = $snap->doCheckStatus(
$checkStatusRequestDto,
$authCode,
$privateKey,
$clientId,
$secretKey,
$isProduction
);
echo json_encode($response, JSON_PRETTY_PRINT);
use Doku\Snap\Models\Refund\RefundRequestDto;
use Doku\Snap\Models\Refund\RefundAdditionalInfoRequestDto;
$additionalInfo = new RefundAdditionalInfoRequestDto("WEB");
$refundAmount = new TotalAmount("100.00", "USD");
$refundRequest = new RefundRequestDto(
$additionalInfo,
"ORIG123",
"EXT456",
$refundAmount,
"Customer request",
"REF789"
);
$authCode = "exampleAuthCode456";
$result = $snap->doRefund($refundRequest, $authCode, $privateKey, $clientId, $secretKey, $isProduction);
echo json_encode($result, JSON_PRETTY_PRINT);
use Doku\Snap\Models\BalanceInquiry\BalanceInquiryRequestDto;
use Doku\Snap\Models\BalanceInquiry\BalanceInquiryAdditionalInfoRequestDto;
$additionalInfo = new BalanceInquiryAdditionalInfoRequestDto("DIRECT_DEBIT_MANDIRI");
$balanceInquiryRequestDto = new BalanceInquiryRequestDto($additionalInfo);
$authCode = "exampleAuthCode123";
$result = $snap->doBalanceInquiry($balanceInquiryRequestDto, $authCode);
echo "Response Code: " . $result->responseCode . PHP_EOL;
echo "Response Message: " . $result->responseMessage . PHP_EOL;
echo "Account Infos: " . PHP_EOL;
foreach ($result->accountInfos as $accountInfo) {
echo " Balance Type: " . $accountInfo->balanceType . PHP_EOL;
echo " Amount: " . $accountInfo->amount->value . " " . $accountInfo->amount->currency . PHP_EOL;
echo " Flat Amount: " . $accountInfo->flatAmount->value . " " . $accountInfo->flatAmount->currency . PHP_EOL;
echo " Hold Amount: " . $accountInfo->holdAmount->value . " " . $accountInfo->holdAmount->currency . PHP_EOL;
echo "---" . PHP_EOL;
}
The SDK throws exceptions for various error conditions. Always wrap your API calls in try-catch blocks:
try {
$result = $snap->createVa($createVaRequestDto);
// Process successful result
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . PHP_EOL;
// Handle the error appropriately
}
To handle token requests in a controller (e.g., in CodeIgniter):
public function tokenRequest()
{
try {
$xSignature = $this->request->getHeaderLine('X-SIGNATURE');
$xTimestamp = $this->request->getHeaderLine('X-TIMESTAMP');
$xClientKey = $this->request->getHeaderLine('X-CLIENT-KEY');
$jsonBody = $this->request->getJSON(true);
// Validate headers and body
if (empty($xSignature) || empty($xTimestamp) || empty($xClientKey)) {
return $this->failUnauthorized('Missing required headers');
}
if (!isset($jsonBody['grantType']) || $jsonBody['grantType'] !== 'client_credentials') {
return $this->failValidationError('Invalid or missing grantType in request body');
}
$isSignatureValid = $this->snap->validateSignature($xSignature, $xTimestamp, $this->privateKey, $xClientKey);
$notificationTokenDTO = $this->snap->generateTokenB2BResponse($isSignatureValid);
$responseBody = $notificationTokenDTO->generateJSONBody();
$headers = $notificationTokenDTO->generateJSONHeader();
// Set response headers
foreach (json_decode($headers) as $name => $value) {
$this->response->setHeader($name, $value);
}
return $this->respond(json_decode($responseBody), 200);
} catch (\Exception $e) {
return $this->fail(['error' => $e->getMessage()], 500);
}
}
To handle payment notifications:
use Doku\Snap\Models\RequestHeader\RequestHeaderDTO;
use Doku\Snap\Models\Notification\PaymentNotificationRequestBodyDTO;
use Doku\Snap\Models\TotalAmount\TotalAmount;
use Doku\Snap\Models\Notification\PaymentNotificationRequestAdditionalInfo;
public function paymentNotification()
{
$jsonBody = $this->request->getJSON(true);
$xRequestId = time() * 1000;
$requestHeaderDTO = new RequestHeaderDTO(
$this->request->getHeaderLine('X-TIMESTAMP'),
$this->request->getHeaderLine('X-SIGNATURE'),
$this->request->getHeaderLine('X-CLIENT-KEY'),
$xRequestId,
"channel_id",
$this->request->getHeaderLine('Authorization')
);
$paidAmount = new TotalAmount(
$jsonBody['paidAmount']['value'] ?? null,
$jsonBody['paidAmount']['currency'] ?? null
);
$additionalInfo = new PaymentNotificationRequestAdditionalInfo(
$jsonBody['additionalInfo']['channel'] ?? null,
$jsonBody['additionalInfo']['senderName'] ?? null,
$jsonBody['additionalInfo']['sourceAccountNo'] ?? null,
$jsonBody['additionalInfo']['sourceBankCode'] ?? null,
$jsonBody['additionalInfo']['sourceBankName'] ?? null
);
$paymentNotificationRequestBodyDTO = new PaymentNotificationRequestBodyDTO(
$jsonBody['partnerServiceId'] ?? '',
$jsonBody['customerNo'] ?? '',
$jsonBody['virtualAccountNo'] ?? '',
$jsonBody['virtualAccountName'] ?? '',
$jsonBody['virtualAccountEmail'] ?? '',
$jsonBody['trxId'] ?? '',
$jsonBody['paymentRequestId'] ?? '',
$paidAmount,
$jsonBody['virtualAccountPhone'] ?? '',
$additionalInfo,
$jsonBody['trxDateTime'] ?? $jsonBody['expiredDate'] ?? '',
$jsonBody['virtualAccountTrxType'] ?? ''
);
$responseDTO = $this->snap->validateTokenAndGenerateNotificationResponse($requestHeaderDTO, $paymentNotificationRequestBodyDTO);
$headers = $responseDTO->generateJSONHeader();
$responseBody = $responseDTO->generateJSONBody();
foreach (json_decode($headers) as $name => $value) {
$this->response->setHeader($name, $value);
}
return $this->respond(json_decode($responseBody));
}
To handle direct inquiries:
public function inquiry()
{
$authorization = $this->request->getHeaderLine('Authorization');
$isValid = $this->snap->validateTokenB2B($authorization);
if ($isValid) {
$requestBody = $this->request->getJSON(true);
$inquiryRequestId = $requestBody['inquiryRequestId'];
$header = $this->snap->generateRequestHeader();
$body = [
"responseCode" => "2002400",
"responseMessage" => "Successful",
"virtualAccountData" => [
"partnerServiceId" => "12362",
"customerNo" => "60000000000000000001",
"virtualAccountNo" => "1236260000000000000000001",
"virtualAccountName" => "Customer Name",
"virtualAccountEmail" => "customer.email@mail.com",
"virtualAccountPhone" => "081293912081",
"totalAmount" => [
"value" => "11500.00",
"currency" => "IDR"
],
"virtualAccountTrxType" => "C",
"expiredDate" => "2023-01-01T10:55:00+07:00",
"additionalInfo" => [
"channel" => "VIRTUAL_ACCOUNT_BRI",
"trxId" => "INV-001"
],
"inquiryStatus" => "00",
"inquiryReason" => [
"english" => "Success",
"indonesia" => "Sukses"
],
"inquiryRequestId" => $inquiryRequestId,
]
];
foreach ($this->request->getHeaders() as $name => $value) {
$this->response->setHeader($name, $value);
}
return $this->respond($body);
} else {
return $this->failUnauthorized('Invalid token');
}
}
To handle notify payment for direct debit:
use Doku\Snap\Models\NotifyPayment\NotifyPaymentDirectDebitRequestDto;
use Doku\Snap\Models\TotalAmount\TotalAmount;
use Doku\Snap\Models\NotifyPayment\PaymentNotificationAdditionalInfoRequestDto;
use Doku\Snap\Models\VA\AdditionalInfo\Origin;
use Doku\Snap\Models\Payment\LineItemsDto;
public function handleDirectDebitNotification()
{
$requestBody = $this->request->getJSON(true);
$xSignature = $this->request->getHeaderLine('X-SIGNATURE');
$xTimestamp = $this->request->getHeaderLine('X-TIMESTAMP');
$amount = new TotalAmount($requestBody['amount']['value'], $requestBody['amount']['currency']);
$lineItems = array_map(function ($item) {
return new LineItemsDto($item['name'], $item['price'], $item['quantity']);
}, $requestBody['additionalInfo']['lineItems']);
$additionalInfo = new PaymentNotificationAdditionalInfoRequestDto(
$requestBody['additionalInfo']['channelId'],
$requestBody['additionalInfo']['acquirerId'],
$requestBody['additionalInfo']['custIdMerchant'],
$requestBody['additionalInfo']['accountType'],
$lineItems,
new Origin()
);
$notifyPaymentRequest = new NotifyPaymentDirectDebitRequestDto(
$requestBody['originalPartnerReferenceNo'],
$requestBody['originalReferenceNo'],
$requestBody['originalExternalId'],
$requestBody['latestTransactionStatus'],
$requestBody['transactionStatusDesc'],
$amount,
$additionalInfo
);
$response = $this->snap->handleDirectDebitNotification($notifyPaymentRequest, $xSignature, $xTimestamp);
return $this->respond($response);
}
- Validate Input: Always validate and sanitize input data before processing.
- Use Try-Catch: Wrap your SDK calls in try-catch blocks to handle exceptions gracefully.
- Log Errors: Log any errors or exceptions for debugging purposes.
- Set Appropriate Headers: Ensure you're setting the correct headers in your responses.
- Handle Token Expiration: Implement logic to refresh tokens when they expire.
- Secure Sensitive Data: Never log or expose sensitive data like tokens or personal information.
By following these patterns and best practices, you can effectively integrate the Doku Snap SDK into your CodeIgniter or similar MVC framework application.