doku/doku-php-library

1.0.2 2024-09-25 04:23 UTC

This package is auto-updated.

Last update: 2024-09-25 04:24:03 UTC


README

本指南演示了如何在PHP项目中集成和使用Doku Snap SDK以执行各种支付操作。

目录

Doku Snap SDK集成指南

目录

  1. 安装
  2. 配置
  3. 使用
    1. 初始化
    2. 令牌管理
    3. 虚拟账户操作
    4. 直接扣款操作
    5. 卡片操作
    6. 其他操作
  4. 错误处理
  5. 高级使用
    1. 处理令牌请求
    2. 处理支付通知
    3. 处理直接查询
    4. 处理直接扣款的支付通知
  6. 控制器最佳实践

安装

使用Composer安装Doku Snap SDK

composer require doku/doku-php-library

配置

在使用Doku Snap SDK之前,您需要使用您的凭据对其进行初始化

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);

使用

初始化

始终从初始化Snap对象开始

$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;
}

错误处理

SDK为各种错误条件抛出异常。始终将您的API调用包裹在try-catch块中

try {
    $result = $snap->createVa($createVaRequestDto);
    // Process successful result
} catch (Exception $e) {
    echo "Error: " . $e->getMessage() . PHP_EOL;
    // Handle the error appropriately
}

高级使用

处理令牌请求

在控制器中处理令牌请求(例如,在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);
    }
}

处理支付通知

处理支付通知

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));
}

处理直接查询

处理直接查询

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');
    }
}

处理直接扣款的支付通知

处理直接扣款的支付通知

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);
}

控制器最佳实践

  1. 验证输入:在处理之前始终验证和清理输入数据。
  2. 使用try-catch:将您的SDK调用包裹在try-catch块中,以优雅地处理异常。
  3. 记录错误:记录任何错误或异常以进行调试。
  4. 设置适当的头信息:确保您在响应中设置了正确的头信息。
  5. 处理令牌过期:实现逻辑以在令牌过期时刷新令牌。
  6. 保护敏感数据:永远不要记录或公开敏感数据,如令牌或个人信息。

遵循这些模式和最佳实践,您可以将Doku Snap SDK有效地集成到CodeIgniter或类似的MVC框架应用中。