Gapura Custom Checkout
Document Version 1.2.1
A customizable integration for businesses to create their own checkout experience. This solution provides the APIs of the Gapura Custom Checkout core system. It is specifically designed for merchants who wants to use their own checkout pages to display payment methods, such as virtual account or QRIS.
Before you start
You will need to register your business in our Merchant Portal to obtain your testing credentials. After you have created your test account, make sure you have done the following:
- Finish your company registration and select Gapura Custom Checkout as your payment solution.
- Setup your webhooks & redirect URLs to receive payment outcomes & redirect user after payment.
- Obtain your testing credentials from the merchant portal.
Process Flow
The general flow of payment using the Gapura payment gateway is as follows:
Visit the Payment Gateway API Overview for edge cases and other scenarios.

- User browses merchant website/app, adds items to cart, and initiates checkout process.
- Merchant queries DANA Consult Pay APIto retrieve current list of supported payment options.
- DANA returns comprehensive list of available payment methods with their details and requirements.
- Merchant presents payment interface showing all available DANA payment options in a user-friendly format.
- User reviews available payment methods and selects their preferred option (e.g., DANA Balance, VA, etc.).
- Merchant system prepares transaction details including amount, items, and user information.
- Merchant sends Create Order API request to DANA API with complete order parameters.
- DANA system performs comprehensive validation of order data and creates transaction record.
- DANA returns detailed order information including payment instructions and transaction reference.
- For Virtual Account and QRIS payments: Merchant displays complete VA or QRIS details with payment instructions.
- Merchant shows e-wallet app or web page checkout page to user.
- User follows payment method-specific steps to complete their transaction.
- DANA sends a payment notification to the merchant's system via the Finish Notify API, updating the payment status of the order.
- DANA redirects to the merchant URL that already set when hitting Create Order API.
- NodeJS
- Python
- Go
- PHP
Step 1 : Library Installation
Visit our Libraries & Plugins guide for detailed information on our SDK.
DANA provides server-side API libraries for several programming languages, available through common package managers, for easier installation and version management. Follow the guide below to install our library:
Requirements
- Node.js version 18 or later
- Your testing credentials from the merchant portal.
Installation
Install using npm or visit our Githubnpm install dana-node@latest --save
Set up the env
PRIVATE_KEY or PRIVATE_KEY_PATH # Your private key
ORIGIN # Your application's origin URL
X_PARTNER_ID # clientId provided during onboarding
ENV # DANA's environment either 'sandbox' or 'production'
Obtaining merchant credentials: Authentication
Step 2 : Initialize the library
Visit our Authentication guide to learn about the authentication process when not using our Library.
Follow the guide below to initialize the library
import { Dana, PaymentGatewayApi as PaymentGatewayApiClient } from 'dana-node';
const danaClient = new Dana({
partnerId: "YOUR_PARTNER_ID", // process.env.X_PARTNER_ID
privateKey: "YOUR_PRIVATE_KEY", // process.env.X_PRIVATE_KEY
origin: "YOUR_ORIGIN", // process.env.ORIGIN
env: "sandbox", // process.env.ENV or "sandbox" or "production"
});
const { PaymentGatewayApi } = danaClient;
Step 3 : Obtain the Available Payment Method via Consult Pay API
Use the Consult Pay API to get a list of available payment methods. In the sandbox environment, all payment methods will be displayed for testing. In production, only the payment methods specified in Perjanjian Kerja Sama (PKS) will be shown.
To consult the available payment method, make a POST
request to the Consult Pay API:
import { Dana } from 'dana-node';
// .. initialize client with authentication
const request: ConsultPayRequest = {
// Fill in required fields here, refer to Consult Pay API Detail
};
const response: ConsultPayResponse = await PaymentGatewayApi.consultPay(request);
Step 4 : Use the Create Order API to get a hosted checkout URL
Use the Create Order API to create new payment requests which will then return the Checkout URL of the hosted payment page.
To create a new order, make a POST request to the Create Order API:
import { Dana } from 'dana-node';
// .. initialize client with authentication
const request: CreateOrderRequest = {
// Fill in required fields here, refer to Create Order API Detail
};
const response: CreateOrderResponse = await PaymentGatewayApi.createOrder(request);
When sending the request parameters, ensure you include payOptionDetails and set additionalInfo.order.scenario to API
.
If successful, the response will include the URL for the hosted payment page. For example:
Content-Type: application/json
X-TIMESTAMP: 2024-12-23T09:10:11+07:00
{
"responseCode": "2005400", // Refer to response code list
"responseMessage": "Successful", // Refer to response code list
"referenceNo": "2020102977770000000009", // Transaction identifier on DANA system
"partnerReferenceNo": "2020102900000000000001", // Transaction identifier on partner system
"webRedirectUrl": "https://pjsp.com/universal?bizNo=REF993883&..."
...
}
After receiving the webRedirectUrl
in the API response, direct your users to the appropriate checkout flow:
- For Virtual Account (VA) or QRIS payments: Display the VA number or QR code to the user on your checkout page.
- For e-wallet payments: Redirect the user to either the e-wallet app or web checkout page.
If you want to simulate payments in the sandbox environment. Contact our Merchant Services team to access our sandbox simulation tools.
Step 5 : Query Order Status, Cancel Order, and Refund Order
Here are the APIs you need to implement for proper integration:
- Query Payment API - Use this API to inquire the latest status of a payment request.
- Cancel Order API - Cancel unpaid orders or paid orders within 24 hours. Cancellation can be initiated by users through the merchant platform or directly through DANA (for DANA Balance payments).
- Refund Order API - Process refunds for completed orders. You can trigger a refund request on behalf of the customer using this API, who will then process the refund through DANA.
import { Dana } from 'dana-node';
//initiate danaClient with authentication
const request: QueryPaymentRequest = {
// Fill in required fields here, refer to Query Payment API Detail
};
const response: QueryPaymentResponse = await PaymentGatewayApi.queryPayment(request);
import { Dana } from 'dana-node';
//initiate danaClient with authentication
const request: CancelOrderRequest = {
// Fill in required fields here, refer to Cancel Order API Detail
};
const response: CancelOrderResponse = await PaymentGatewayApi.cancelOrder(request);
import { Dana } from 'dana-node';
//initiate danaClient with authentication
const request: RefundOrderRequest = {
// Fill in required fields here, refer to Refund Order API Detail
};
const response: RefundOrderResponse = await PaymentGatewayApi.refundOrder(request);
Step 6 : Receive Payment Outcome via Finish Notify API
After a successful payment:
- The user will be redirected to your specified Redirect URL, which you can configure using the
urlParams
parameter in the Create Order API API request. - DANA will send payment notifications to your Notification URL via the Finish Notify API. Configure your notification endpoint with the ASPI-mandated path format:
/v1.0/debit/notify
.
Construction
new WebhookParser(publicKey?: string, publicKeyPath?: string)
Request
Parameter | Type | Remarks |
---|---|---|
publicKey | string | The DANA gateway's public key as a PEM formatted string. This is used if publicKeyPath is not provided or is empty |
publicKeyPath | string | The file path to the DANA gateway's public key PEM file. If provided, this will be prioritized over the publicKey string |
Notes: One of publicKey
or publicKeyPath
must be provided.
Method
parseWebhook(httpMethod: string, relativePathUrl: string, headers: { [key: string]: string }, body: string): FinishNotifyRequest
Request
Parameter | Type | Remarks |
---|---|---|
httpMethod | string | The HTTP method of the incoming webhook request e.g., POST |
relative_path_url | string | The relative URL path of the webhook endpoint that received the notification e.g /v1.0/debit/notify |
headers | map[string]string | A map containing the HTTP request headers. This map must include X-SIGNATURE and X-TIMESTAMP headers provided by DANA for signature verification |
body | string | The raw JSON string payload from the webhook request body |
- Returns: A pointer to a
FinishNotifyRequest
struct containing the parsed and verified webhook data, or an error if parsing or signature verification fails. - Raises:
ValueError
if signature verification fails or the payload is invalid.
Security Notes
- Always use the official public key provided by DANA for webhook verification.
- Reject any webhook requests that fail signature verification or have malformed payloads.
- Never trust webhook data unless it passes verification.
import { WebhookParser } from 'dana-node/dist/webhook'; // Adjust import path as needed
async function handleDanaWebhook(req: AnyRequestType, res: AnyResponseType) {
// Retrieve the DANA public key from environment variables or a secure configuration.
// Option 1: Public key as a string
const danaPublicKeyString: string | undefined = process.env.DANA_WEBHOOK_PUBLIC_KEY_STRING;
// Option 2: Path to the public key file (recommended for production)
const danaPublicKeyPath: string | undefined = process.env.DANA_WEBHOOK_PUBLIC_KEY_PATH;
if (!danaPublicKeyString && !danaPublicKeyPath) {
console.error('DANA webhook public key not configured.');
res.status(500).send('Webhook processor configuration error.'); // Or appropriate error handling
return;
}
const httpMethod: string = req.method!; // e.g., "POST"
const relativePathUrl: string = req.path!; // e.g., "/v1.0/debit/notify". Ensure this is the path DANA signs.
const headers: Record<string, string> = req.headers as Record<string, string>;
let requestBodyString: string;
if (typeof req.body === 'string') {
requestBodyString = req.body;
} else if (req.body && typeof req.body === 'object') {
requestBodyString = JSON.stringify(req.body);
} else {
console.error('Request body is not a string or a parseable object.');
res.status(400).send('Invalid request body format.');
return;
}
// Initialize WebhookParser.
const parser = new WebhookParser(danaPublicKeyString, danaPublicKeyPath);
try {
// Verify the signature and parse the webhook payload
const finishNotify = parser.parseWebhook(
httpMethod,
relativePathUrl,
headers,
requestBodyString
);
console.log('Webhook verified successfully:');
console.log('Original Partner Reference No:', finishNotify.originalPartnerReferenceNo);
// TODO: Process the finishNotify object (e.g., update order status in your database)
res.status(200).send('Webhook received and verified.');
} catch (error: any) { // Catching as 'any' to access error.message
console.error('Webhook verification failed:', error.message);
// Respond with an error status. DANA might retry if it receives an error.
res.status(400).send(`Webhook verification failed: ${error.message}`);
}
}
For detailed example, please refer to the following resource: Example Webhook.
Example of a successful payment webhook payload:
Content-Type: application/json
X-TIMESTAMP: 2024-12-23T09:10:11+07:00
{
"responseCode": "2005400", // Refer to response code list
"responseMessage": "Successful", // Refer to response code list
"referenceNo": "2020102977770000000009", // Transaction identifier on DANA system
"partnerReferenceNo": "2020102900000000000001", // Transaction identifier on partner system
"webRedirectUrl": "https://pjsp.com/universal?bizNo=REF993883&..."
...
}
Additional Enum Configuration
The library provides several enums (enumerations) to represent a fixed set of constant values, ensuring consistency and reducing errors during integration.
import { EnvInfoSourcePlatformEnum } from 'dana-node/dist/payment_gateway/v1';
const ipg = EnvInfoSourcePlatformEnum.Ipg;
The following enums are available in the Library Payment Gateway:
- AcquirementStatusEnum
- ActorTypeEnum
- OrderTerminalTypeEnum
- PayMethodEnum
- PayOptionEnum
- ScenarioEnum
- SourcePlatformEnum
- TerminalTypeEnum
- TypeEnum
Step 7 : Automated UAT Testing Suite
To verify your integration, run our automated test suite. It takes under 2 minutes to tests your integration with mandated test scenarios. Check out the Github repo for more instructions
Step 8 : Apply for Live Payment
As part of regulatory compliance, merchants are required to submit UAT testing documents to meet Bank Indonesia's requirements. After completing sandbox testing, follow these steps to move to production:
Generate production keys
Create your production private and public keys, follow this instruction: Authentication - Production Credential.Confirm UAT testing logs
Confirm that you have completed all testing scenarios from our Merchant Portal.Fill go-live submission form
Follow the instructions inside our Merchant Portal to apply for production credentials. We will process your application in 1-2 days.Obtain production credentials
Once approved, you will receive your production credentials such as: Merchant ID, Client ID known as X-PARTNER-ID, and Client Secret.
Testing in production environment
Configure production environment
Switch your application settings from sandbox to production environment by updating the API endpoints and credentials.Test using production credentials
Conduct the same testing scenarios as sandbox testing, using your production credentials.UAT production sign-off
Once testing is complete, DANA will prepare the UAT Production Sign Off document in the Merchant Portal. Both merchant and DANA representatives must sign this document to formally approve the integration.Go-live
After receiving all approvals, your DANA integration will be activated and ready for live payments from your customers.
Ready to submit testing documents?
Access our merchant portal for detailed guide to start receiving live payments
Step 1 : Library Installation
Visit our Libraries & Plugins guide for detailed information on our SDK.
DANA provides server-side API libraries for several programming languages, available through common package managers, for easier installation and version management. Follow the guide below to install our library:
Requirements
- Python 3.9.1+
- Your testing credentials from the merchant portal.
Installation
Install using pip or visit our Githubpip install dana-python
Set up the env
PRIVATE_KEY or PRIVATE_KEY_PATH # Your private key
ORIGIN # Your application's origin URL
X_PARTNER_ID # clientId provided during onboarding
ENV # DANA's environment either 'sandbox' or 'production'
Obtaining merchant credentials: Authentication
Import Package
import dana.payment_gateway.v1
Step 2 : Initialize the library
Visit our Authentication guide to learn about the authentication process when not using our Library.
Follow the guide below to initialize the library
import os
from dana.utils.snap_configuration import SnapConfiguration, AuthSettings, Env
configuration = SnapConfiguration(
api_key=AuthSettings(
PRIVATE_KEY=os.environ.get("PRIVATE_KEY"), # obtained from Merchant Portal
ORIGIN=os.environ.get("ORIGIN"), # Origin domain
X_PARTNER_ID=os.environ.get("X_PARTNER_ID"), # known as clientId
ENV=Env.SANDBOX # environment either 'sandbox' or 'production'
)
)
Step 3 : Obtain the Available Payment Method via Consult Pay API
Use the Consult Pay API to get a list of available payment methods. In the sandbox environment, all payment methods will be displayed for testing. In production, only the payment methods specified in Perjanjian Kerja Sama (PKS) will be shown.
To consult the available payment method, make a POST
request to the Consult Pay API:
from dana.payment_gateway.v1.models.ConsultPayRequest import ConsultPayRequest
from dana.payment_gateway.v1 import PaymentGatewayApi
from dana.api_client import ApiClient
from dana.rest import ApiException
with ApiClient(configuration) as api_client:
api_instance = PaymentGatewayApi(api_client)
consult_pay_request = ConsultPayRequest(
# Fill in required fields here, refer to Consult Pay API Detail.
)
try:
api_response = api_instance.consult_pay(consult_pay_request)
except Exception as e:
print(e)
Step 4 : Use the Create Order API to get a hosted checkout URL
Use the Create Order API to create new payment requests which will then return the Checkout URL of the hosted payment page.
To create a new order, make a POST request to the Create Order API:
from dana.payment_gateway.v1.models.create_order_by_redirect_request import CreateOrderByRedirectRequest
from dana.payment_gateway.v1 import PaymentGatewayApi
from dana.api_client import ApiClient
from dana.rest import ApiException
with ApiClient(configuration) as api_client:
api_instance = PaymentGatewayApi(api_client)
create_order_request = CreateOrderByRedirectRequest(
# Fill in required fields here, refer to Create Order API Detail.
)
try:
api_response = api_instance.create_order(create_order_request)
except Exception as e:
print(e)
Important: When sending the request parameters, ensure you include payOptionDetails and set additionalInfo.order.scenario to API
.
If successful, the response will include the URL for the hosted payment page. For example:
Content-Type: application/json
X-TIMESTAMP: 2024-12-23T09:10:11+07:00
{
"responseCode": "2005400", // Refer to response code list
"responseMessage": "Successful", // Refer to response code list
"referenceNo": "2020102977770000000009", // Transaction identifier on DANA system
"partnerReferenceNo": "2020102900000000000001", // Transaction identifier on partner system
"webRedirectUrl": "https://pjsp.com/universal?bizNo=REF993883&..."
...
}
After receiving the webRedirectUrl
in the API response, direct your users to the appropriate checkout flow:
- For Virtual Account (VA) or QRIS payments: Display the VA number or QR code to the user on your checkout page.
- For e-wallet payments: Redirect the user to either the e-wallet app or web checkout page.
If you want to simulate payments in the sandbox environment. Contact our Merchant Services team to access our sandbox simulation tools.
Step 5 : Query Order Status, Cancel Order, and Refund Order
Here are the APIs you need to implement for proper integration:
- Query Payment API - Use this API to inquire the latest status of a payment request.
- Cancel Order API - Cancel unpaid orders or paid orders within 24 hours. Cancellation can be initiated by users through the merchant platform or directly through DANA (for DANA Balance payments).
- Refund Order API - Process refunds for completed orders. You can trigger a refund request on behalf of the customer using this API, who will then process the refund through DANA.
import os
from dana.utils.snap_configuration import SnapConfiguration, AuthSettings, Env
from dana.payment_gateway.v1 import PaymentGatewayApi
from dana.payment_gateway.v1.models.QueryPaymentRequest import QueryPaymentRequest
from dana.api_client import ApiClient
from dana.rest import ApiException
# Configuration
# initiate authentication in configuration
# Query Payment
with ApiClient(configuration) as api_client:
api_instance = PaymentGatewayApi(api_client)
query_payment_request = QueryPaymentRequest(
# Fill in required fields here, such as order_id
order_id="YOUR_ORDER_ID"
...
)
try:
api_response = api_instance.query_payment(query_payment_request)
except ApiException as e:
print(e)
from dana.payment_gateway.v1 import PaymentGatewayApi
from dana.payment_gateway.v1.models.CancelOrderRequest import CancelOrderRequest
from dana.api_client import ApiClient
from pprint import pprint
# Cancel Order
with ApiClient(configuration) as api_client:
api_instance = PaymentGatewayApi(api_client)
cancel_order_request = CancelOrderRequest(
# Fill in required fields here, for the detailed request refer to Cancel Order API Detail
)
try:
api_response = api_instance.cancel_order(cancel_order_request)
print("The response of PaymentGatewayApi->cancel_order:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling PaymentGatewayApi->cancel_order: %s\n" % e)
from dana.payment_gateway.v1 import PaymentGatewayApi
from dana.payment_gateway.v1.models.RefundOrderRequest import RefundOrderRequest
from dana.api_client import ApiClient
from pprint import pprint
# Refund Order
with ApiClient(configuration) as api_client:
api_instance = PaymentGatewayApi(api_client)
refund_order_request = RefundOrderRequest(
# Fill in required fields here, for the detailed request refer to Refund Order API Detail
)
try:
api_response = api_instance.refund_order(refund_order_request)
print("The response of PaymentGatewayApi->refund_order:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling PaymentGatewayApi->refund_order: %s\n" % e)
Step 6 : Receive Payment Outcome via Finish Notify API
After a successful payment:
- The user will be redirected to your specified Redirect URL, which you can configure using the
urlParams
parameter in the Create Order API API request. - DANA will send payment notifications to your Notification URL via the Finish Notify API. Configure your notification endpoint with the ASPI-mandated path format:
/v1.0/debit/notify
.
Construction
WebhookParser(gateway_public_key_pem: str)
Request
Parameter | Type | Remarks |
---|---|---|
gateway_public_key_pem | string | The DANA gateway's public key in PEM format. Used to verify the webhook signature. |
Method
parse_webhook(http_method: str, relative_path_url: str, headers: dict, body: str) -> FinishNotify
Request
Parameter | Type | Remarks |
---|---|---|
httpMethod | string | The HTTP method of the incoming webhook request e.g., http.MethodPost |
relative_path_url | string | The relative URL path of the webhook endpoint that received the notification e.g /v1.0/debit/notify |
headers | map[string]string | A map containing the HTTP request headers. This map must include X-SIGNATURE and X-TIMESTAMP headers provided by DANA for signature verification |
body | string | The raw JSON string payload from the webhook request body |
- Returns: A pointer to a
model.FinishNotify
struct containing the parsed and verified webhook data, or an error if parsing or signature verification fails. - Raises:
ValueError
if signature verification fails or the payload is invalid.
Security Notes
- Always use the official public key provided by DANA for webhook verification.
- Reject any webhook requests that fail signature verification or have malformed payloads.
- Never trust webhook data unless it passes verification.
import os
from dana.webhook import WebhookParser
# Replace with your actual DANA public key (PEM format)
DANA_PUBLIC_KEY = os.getenv("DANA_PUBLIC_KEY")
# Example HTTP request data from your webhook handler
http_method = "POST"
relative_path_url = "/v1.0/debit/notify"
headers = {
"X-SIGNATURE": "<signature-from-header>",
"X-TIMESTAMP": "<timestamp-from-header>"
}
body = '{"original_partner_reference_no": "123...", ...}' # Raw JSON string from request body
parser = WebhookParser(DANA_PUBLIC_KEY)
try:
finish_notify = parser.parse_webhook(
http_method=http_method,
relative_path_url=relative_path_url,
headers=headers,
body=body
)
print(finish_notify.original_partner_reference_no)
except ValueError as e:
print(f"Webhook verification failed: {e}")
Example of a successful payment webhook payload:
Content-Type: application/json
X-TIMESTAMP: 2024-12-23T09:10:11+07:00
{
"responseCode": "2005400", // Refer to response code list
"responseMessage": "Successful", // Refer to response code list
"referenceNo": "2020102977770000000009", // Transaction identifier on DANA system
"partnerReferenceNo": "2020102900000000000001", // Transaction identifier on partner system
"webRedirectUrl": "https://pjsp.com/universal?bizNo=REF993883&..."
...
}
Step 7 : Automated UAT Testing Suite
To verify your integration, run our automated test suite. It takes under 2 minutes to tests your integration with mandated test scenarios. Check out the Github repo for more instructions
Step 8 : Apply for Live Payment
As part of regulatory compliance, merchants are required to submit UAT testing documents to meet Bank Indonesia's requirements. After completing sandbox testing, follow these steps to move to production:
Generate production keys
Create your production private and public keys, follow this instruction: Authentication - Production Credential.Confirm UAT testing logs
Confirm that you have completed all testing scenarios from our Merchant Portal.Fill go-live submission form
Follow the instructions inside our Merchant Portal to apply for production credentials. We will process your application in 1-2 days.Obtain production credentials
Once approved, you will receive your production credentials such as: Merchant ID, Client ID known as X-PARTNER-ID, and Client Secret.
Testing in production environment
Configure production environment
Switch your application settings from sandbox to production environment by updating the API endpoints and credentials.Test using production credentials
Conduct the same testing scenarios as sandbox testing, using your production credentials.UAT production sign-off
Once testing is complete, DANA will prepare the UAT Production Sign Off document in the Merchant Portal. Both merchant and DANA representatives must sign this document to formally approve the integration.Go-live
After receiving all approvals, your DANA integration will be activated and ready for live payments from your customers.
Ready to submit testing documents?
Access our merchant portal for detailed guide to start receiving live payments
Step 1 : Library Installation
Visit our Libraries & Plugins guide for detailed information on our SDK.
DANA provides server-side API libraries for several programming languages, available through common package managers, for easier installation and version management. Follow the guide below to install our library:
Requirements
- go.mod
- go.sum file
- Your testing credentials from the merchant portal.
Installation
Install or visit our Githubgo get github.com/dana-id/dana-go
Set up the env
PRIVATE_KEY or PRIVATE_KEY_PATH # Your private key
ORIGIN # Your application's origin URL
X_PARTNER_ID # clientId provided during onboarding
ENV # DANA's environment either 'sandbox' or 'production'
Obtaining merchant credentials: Authentication
Import Package
import (
payment_gateway "github.com/dana-id/dana-go/payment_gateway/v1"
)
Step 2 : Initialize the library
Visit our Authentication guide to learn about the authentication process when not using our Library.
Follow the guide below to initialize the library
package main
import (
"context"
"fmt"
"os"
dana "github.com/dana-id/dana-go"
"github.com/dana-id/dana-go/config"
payment_gateway "github.com/dana-id/dana-go/payment_gateway/v1"
)
func main() {
// Configuring api client
// Api client should be singleton, can reuse the apiClient for multiple requests in various operations
configuration := config.NewConfiguration()
configuration.APIKey = &config.APIKey{
// ENV: config.ENV_SANDBOX, // use config.ENV_PRODUCTION for production. Can use DANA_ENV instead
DANA_ENV: config.ENV_SANDBOX, // use config.ENV_PRODUCTION for production
X_PARTNER_ID: os.Getenv("X_PARTNER_ID"),
PRIVATE_KEY: os.Getenv("PRIVATE_KEY"), // Can provide the private key directly as a string or via a file path (PRIVATE_KEY_PATH). If both added, we will prioritize the path
ORIGIN: os.Getenv("ORIGIN"),
// PRIVATE_KEY_PATH: os.Getenv("PRIVATE_KEY_PATH"),
}
apiClient := dana.NewAPIClient(configuration)
}
Step 3 : Obtain the Available Payment Method via Consult Pay API
Use the Consult Pay API to get a list of available payment methods. In the sandbox environment, all payment methods will be displayed for testing. In production, only the payment methods specified in Perjanjian Kerja Sama (PKS) will be shown.
To consult the available payment method, make a POST
request to the Consult Pay API:
package main
import (
"context"
"fmt"
"os"
dana "github.com/dana-id/dana-go"
"github.com/dana-id/dana-go/config"
payment_gateway "github.com/dana-id/dana-go/payment_gateway/v1"
)
func main() {
// ... define authentication
request := payment_gateway.ConsultPayRequest{
// Fill in required fields here, refer to Consult Pay API Detail
}
_, r, err := apiClient.PaymentGatewayAPI.ConsultPay(context.Background()).ConsultPayRequest(request).Execute()
if err != nil {
fmt.Fprintf(os.Stderr, "Error when calling `PaymentGatewayAPI.ConsultPay``: %v\n", err)
fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r)
}
// response from `ConsultPay`: ConsultPayResponse
fmt.Fprintf(os.Stdout, "Response from `PaymentGatewayAPI.ConsultPay`: %v\n", r.Body)
}
Step 4 : Use the Create Order API to get a hosted checkout URL
Use the Create Order API to create new payment requests which will then return the Checkout URL of the hosted payment page.
To create a new order, make a POST request to the Create Order API:
package main
import (
"context"
"fmt"
"os"
dana "github.com/dana-id/dana-go"
"github.com/dana-id/dana-go/config"
payment_gateway "github.com/dana-id/dana-go/payment_gateway/v1"
)
func main() {
// ... define authentication
request := payment_gateway.CreateOrderRequest{
// Fill in required fields here, refer to Create Order API Detail
}
_, r, err := apiClient.PaymentGatewayAPI.CreateOrder(context.Background()).CreateOrderRequest(request).Execute()
if err != nil {
fmt.Fprintf(os.Stderr, "Error when calling `PaymentGatewayAPI.CreateOrder``: %v\n", err)
fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r)
}
// response from `CreateOrder`: CreateOrderResponse
fmt.Fprintf(os.Stdout, "Response from `PaymentGatewayAPI.CreateOrder`: %v\n", r.Body)
}
Important: When sending the request parameters, ensure you include payOptionDetails and set additionalInfo.order.scenario to API
.
If successful, the response will include the URL for the hosted payment page. For example:
Content-Type: application/json
X-TIMESTAMP: 2024-12-23T09:10:11+07:00
{
"responseCode": "2005400", // Refer to response code list
"responseMessage": "Successful", // Refer to response code list
"referenceNo": "2020102977770000000009", // Transaction identifier on DANA system
"partnerReferenceNo": "2020102900000000000001", // Transaction identifier on partner system
"webRedirectUrl": "https://pjsp.com/universal?bizNo=REF993883&..."
...
}
After receiving the webRedirectUrl
in the API response, direct your users to the appropriate checkout flow:
- For Virtual Account (VA) or QRIS payments: Display the VA number or QR code to the user on your checkout page.
- For e-wallet payments: Redirect the user to either the e-wallet app or web checkout page.
If you want to simulate payments in the sandbox environment. Contact our Merchant Services team to access our sandbox simulation tools.
Step 5 : Query Order Status, Cancel Order, and Refund Order
Here are the APIs you need to implement for proper integration:
- Query Payment API - Use this API to inquire the latest status of a payment request.
- Cancel Order API - Cancel unpaid orders or paid orders within 24 hours. Cancellation can be initiated by users through the merchant platform or directly through DANA (for DANA Balance payments).
- Refund Order API - Process refunds for completed orders. You can trigger a refund request on behalf of the customer using this API, who will then process the refund through DANA.
package main
import (
"context"
"fmt"
"os"
dana "github.com/dana-id/dana-go"
"github.com/dana-id/dana-go/config"
payment_gateway "github.com/dana-id/dana-go/payment_gateway/v1"
)
func main() {
// ... define authentication
request := payment_gateway.QueryPaymentRequest{
// Fill in required fields here, refer to Query Payment API Detail
}
_, r, err := apiClient.PaymentGatewayAPI.QueryPayment(context.Background()).QueryPaymentRequest(request).Execute()
if err != nil {
fmt.Fprintf(os.Stderr, "Error when calling `PaymentGatewayAPI.QueryPayment``: %v\n", err)
fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r)
}
// response from `QueryPayment`: QueryPaymentResponse
fmt.Fprintf(os.Stdout, "Response from `PaymentGatewayAPI.QueryPayment`: %v\n", r.Body)
}
package main
import (
"context"
"fmt"
"os"
dana "github.com/dana-id/dana-go"
"github.com/dana-id/dana-go/config"
payment_gateway "github.com/dana-id/dana-go/payment_gateway/v1"
)
func main() {
// ... define authentication
request := payment_gateway.CancelOrderRequest{
// Fill in required fields here, refer to Cancel Order API Detail
}
_, r, err := apiClient.PaymentGatewayAPI.CancelOrder(context.Background()).CancelOrderRequest(request).Execute()
if err != nil {
fmt.Fprintf(os.Stderr, "Error when calling `PaymentGatewayAPI.CancelOrder``: %v\n", err)
fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r)
}
// response from `CancelOrder`: CancelOrderResponse
fmt.Fprintf(os.Stdout, "Response from `PaymentGatewayAPI.CancelOrder`: %v\n", r.Body)
}
package main
import (
"context"
"fmt"
"os"
dana "github.com/dana-id/dana-go"
"github.com/dana-id/dana-go/config"
payment_gateway "github.com/dana-id/dana-go/payment_gateway/v1"
)
func main() {
// ... define authentication
request := payment_gateway.RefundOrderRequest{
// Fill in required fields here, refer to Refund Order API Detail
}
_, r, err := apiClient.PaymentGatewayAPI.RefundOrder(context.Background()).RefundOrderRequest(request).Execute()
if err != nil {
fmt.Fprintf(os.Stderr, "Error when calling `PaymentGatewayAPI.RefundOrder``: %v\n", err)
fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r)
}
// response from `RefundOrder`: RefundOrderResponse
fmt.Fprintf(os.Stdout, "Response from `PaymentGatewayAPI.RefundOrder`: %v\n", r.Body)
}
Step 6 : Receive Payment Outcome via Finish Notify API
After a successful payment:
- The user will be redirected to your specified Redirect URL, which you can configure using the
urlParams
parameter in the Create Order API API request. - DANA will send payment notifications to your Notification URL via the Finish Notify API. Configure your notification endpoint with the ASPI-mandated path format:
/v1.0/debit/notify
.
Construction
func NewWebhookParser(publicKey *string, publicKeyPath *string) (*WebhookParser, error)
Request
Parameter | Type | Remarks |
---|---|---|
publicKey | string | The DANA gateway's public key as a PEM formatted string. This is used if publicKeyPath is not provided or is empty. |
publicKeyPath | string | The file path to the DANA gateway's public key PEM file. If provided, this will be prioritized over the publicKey string. |
- Returns: A pointer to a
WebhookParser
instance and an error if the public key is invalid.
Method
func (p *WebhookParser) ParseWebhook(httpMethod string, relativePathURL string, headers map[string]string, body string) (*webhook.FinishNotify, error)
Request
Parameter | Type | Remarks |
---|---|---|
httpMethod | string | The HTTP method of the incoming webhook request e.g., http.MethodPost |
relativePathURL | string | The relative URL path of the webhook endpoint that received the notification e.g /v1.0/debit/notify |
headers | map[string]string | A map containing the HTTP request headers. This map must include X-SIGNATURE and X-TIMESTAMP headers provided by DANA for signature verification |
body | string | The raw JSON string payload from the webhook request body |
- Return: A pointer to a
model.FinishNotifyRequest
struct containing the parsed and verified webhook data, or an error if parsing or signature verification fails.
Security Notes
- Always use the official public key provided by DANA for webhook verification. Store and load it securely.
- The
ParseWebhook
method handles both JSON parsing and cryptographic signature verification. If it returns an error, the payload should not be trusted.
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
webhook "github.com/dana-id/dana-go/webhook"
)
// This function would be your actual webhook handler in a real application.
func webhookNotificationHandler(req *http.Request) {
// 1. Initialize the WebhookParser
// You can provide the public key directly as a string or via a file path.
// The parser will prioritize publicKeyPath if both are provided.
// Option 1: Provide public key as a string
// danaPublicKeyPEM := os.Getenv("DANA_PUBLIC_KEY")
// parser, err := webhook.NewWebhookParser(&danaPublicKeyPEM, nil)
// Option 2: Provide path to public key file
danaPublicKeyPath := os.Getenv("DANA_PUBLIC_KEY_PATH") // e.g., "/path/to/your/dana_public_key.pem"
parser, err := webhook.NewWebhookParser(nil, &danaPublicKeyPath)
if err != nil {
fmt.Printf("Error creating WebhookParser: %v\n", err)
return
}
// 2. Extract data from the incoming HTTP Request
httpMethod := req.Method
relativePathUrl := "/v1.0/debit/notify"
// relativePathUrl := req.URL.Path // This should match the path DANA sends the webhook to for example: /v1.0/debit/notify
// Read the request body
bodyBytes, err := io.ReadAll(req.Body)
if err != nil {
fmt.Printf("Error reading request body: %v\n", err)
return
}
defer req.Body.Close() // Important to close the body
webhookBodyStr := string(bodyBytes)
// Log received data for debugging (optional)
fmt.Printf("Received webhook: Method=%s, Path=%s, Headers=%v, Body=%s\n",
httpMethod, relativePathUrl, req.Header, webhookBodyStr)
// 3. Parse and verify the webhook
parsedData, err := parser.ParseWebhook(
httpMethod,
relativePathUrl,
req.Header,
webhookBodyStr,
)
if err != nil {
fmt.Printf("Webhook parsing/verification failed: %v\n", err)
// IMPORTANT: If verification fails, do not trust the payload.
return
}
// 4. Use the parsed data
fmt.Printf("Webhook parsed successfully!\n")
fmt.Printf("Original Partner Reference No: %s\n", parsedData.OriginalPartnerReferenceNo)
fmt.Printf("Amount: %s %s\n", parsedData.Amount.Value, parsedData.Amount.Currency)
fmt.Printf("Status: %s\n", parsedData.LatestTransactionStatus)
// Access other fields from parsedData as needed
}
For detailed example, please refer to the following resource: Example Webhook.
Example of a successful payment webhook payload:
Content-Type: application/json
X-TIMESTAMP: 2024-12-23T09:10:11+07:00
{
"responseCode": "2005400", // Refer to response code list
"responseMessage": "Successful", // Refer to response code list
"referenceNo": "2020102977770000000009", // Transaction identifier on DANA system
"partnerReferenceNo": "2020102900000000000001", // Transaction identifier on partner system
"webRedirectUrl": "https://pjsp.com/universal?bizNo=REF993883&..."
...
}
Additional Enum Configuration
The library provides several enums (enumerations) to represent a fixed set of constant values, ensuring consistency and reducing errors during integration.
import payment_gateway "github.com/dana-id/dana-go/payment_gateway/v1"
ipg := string(payment_gateway.SOURCEPLATFORM_IPG_)
The following enums are available in the Library Payment Gateway:
- acquirementStatus
- actorType
- orderTerminalType
- payMethod
- payOption
- sourcePlatform
- terminalType
- type
Step 7 : Automated UAT Testing Suite
To verify your integration, run our automated test suite. It takes under 2 minutes to tests your integration with mandated test scenarios. Check out the Github repo for more instructions
Step 8 : Apply for Live Payment
As part of regulatory compliance, merchants are required to submit UAT testing documents to meet Bank Indonesia's requirements. After completing sandbox testing, follow these steps to move to production:
Generate production keys
Create your production private and public keys, follow this instruction: Authentication - Production Credential.Confirm UAT testing logs
Confirm that you have completed all testing scenarios from our Merchant Portal.Fill go-live submission form
Follow the instructions inside our Merchant Portal to apply for production credentials. We will process your application in 1-2 days.Obtain production credentials
Once approved, you will receive your production credentials such as: Merchant ID, Client ID known as X-PARTNER-ID, and Client Secret.
Testing in production environment
Configure production environment
Switch your application settings from sandbox to production environment by updating the API endpoints and credentials.Test using production credentials
Conduct the same testing scenarios as sandbox testing, using your production credentials.UAT production sign-off
Once testing is complete, DANA will prepare the UAT Production Sign Off document in the Merchant Portal. Both merchant and DANA representatives must sign this document to formally approve the integration.Go-live
After receiving all approvals, your DANA integration will be activated and ready for live payments from your customers.
Ready to submit testing documents?
Access our merchant portal for detailed guide to start receiving live payments
Step 1 : Library Installation
Visit our Libraries & Plugins guide for detailed information on our SDK.
DANA provides server-side API libraries for several programming languages, available through common package managers, for easier installation and version management. Follow the guide below to install our library:
Requirements
- PHP 7.4+, compatible with PHP 8.0.
- Your testing credentials from the merchant portal.
Installation
Install using composer or visit our Github- Using Composer
- Add the following code to
composer.json
- Add the following code to
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/GIT_USER_ID/GIT_REPO_ID.git"
}
],
"require": {
"GIT_USER_ID/GIT_REPO_ID": "*@dev"
}
}
- Run
composer install
- Manual Installation
<?php
require_once('/path/to/DanaPhp/vendor/autoload.php');
Set up the env
PRIVATE_KEY or PRIVATE_KEY_PATH # Your private key
ORIGIN # Your application's origin URL
X_PARTNER_ID # clientId provided during onboarding
ENV # DANA's environment either 'sandbox' or 'production'
Obtaining merchant credentials: Authentication
Import Package
use Dana\PaymentGateway\v1
Step 2 : Initialize the library
Visit our Authentication guide to learn about the authentication process when not using our Library.
Follow the guide below to initialize the library
<?php
use Dana\Configuration;
use Dana\Env;
use Dana\PaymentGateway\v1\Api\PaymentGatewayApi;
// Set up configuration with authentication settings
$configuration = new Configuration();
// The Configuration constructor automatically loads values from environment variables
// Choose one of PRIVATE_KEY or PRIVATE_KEY_PATH to set, if you set both, PRIVATE_KEY will be ignored
$configuration->setApiKey('PRIVATE_KEY', getenv('PRIVATE_KEY'));
// $configuration->setApiKey('PRIVATE_KEY_PATH', getenv('PRIVATE_KEY_PATH'));
$configuration->setApiKey('ORIGIN', getenv('ORIGIN'));
$configuration->setApiKey('X_PARTNER_ID', getenv('X_PARTNER_ID'));
$configuration->setApiKey('DANA_ENV', Env::SANDBOX);
// Choose one of ENV or DANA_ENV to set, if you set both, ENV will be ignored
// $configuration->setApiKey('ENV', Env::SANDBOX);
Step 3 : Obtain the Available Payment Method via Consult Pay API
Use the Consult Pay API to get a list of available payment methods. In the sandbox environment, all payment methods will be displayed for testing. In production, only the payment methods specified in Perjanjian Kerja Sama (PKS) will be shown.
To consult the available payment method, make a POST
request to the Consult Pay API:
<?php
use Dana\Configuration;
use Dana\Env;
use Dana\PaymentGateway\v1\Api\PaymentGatewayApi;
use Dana\PaymentGateway\v1\Model\ConsultPayRequest;
// ... define authentication
$consultPayRequest = ConsultPayRequest();
try {
$result = $apiInstance->consultPay($consultPayRequest);
print_r($result);
} catch (Exception $e) {
echo 'Exception when calling PaymentGatewayApi->consultPay: ', $e->getMessage(), PHP_EOL;
}
Step 4 : Use the Create Order API to get a hosted checkout URL
Use the Create Order API to create new payment requests which will then return the Checkout URL of the hosted payment page.
To create a new order, make a POST request to the Create Order API:
<?php
use Dana\Configuration;
use Dana\Env;
use Dana\PaymentGateway\v1\Api\PaymentGatewayApi;
use Dana\PaymentGateway\v1\Model\CreateOrderRequest;
// ... define authentication
$createOrderRequest = CreateOrderRequest();
try {
$result = $apiInstance->createOrder($createOrderRequest);
print_r($result);
} catch (Exception $e) {
echo 'Exception when calling PaymentGatewayApi->createOrder: ', $e->getMessage(), PHP_EOL;
}
When sending the request parameters, ensure you include payOptionDetails and set additionalInfo.order.scenario to API
.
If successful, the response will include the URL for the hosted payment page. For example:
Content-Type: application/json
X-TIMESTAMP: 2024-12-23T09:10:11+07:00
{
"responseCode": "2005400", // Refer to response code list
"responseMessage": "Successful", // Refer to response code list
"referenceNo": "2020102977770000000009", // Transaction identifier on DANA system
"partnerReferenceNo": "2020102900000000000001", // Transaction identifier on partner system
"webRedirectUrl": "https://pjsp.com/universal?bizNo=REF993883&..."
...
}
After receiving the webRedirectUrl
in the API response, direct your users to the appropriate checkout flow:
- For Virtual Account (VA) or QRIS payments: Display the VA number or QR code to the user on your checkout page.
- For e-wallet payments: Redirect the user to either the e-wallet app or web checkout page.
If you want to simulate payments in the sandbox environment. Contact our Merchant Services team to access our sandbox simulation tools.
Step 5 : Query Order Status, Cancel Order, and Refund Order
Here are the APIs you need to implement for proper integration:
- Query Payment API - Use this API to inquire the latest status of a payment request.
- Cancel Order API - Cancel unpaid orders or paid orders within 24 hours. Cancellation can be initiated by users through the merchant platform or directly through DANA (for DANA Balance payments).
- Refund Order API - Process refunds for completed orders. You can trigger a refund request on behalf of the customer using this API, who will then process the refund through DANA.
<?php
use Dana\Configuration;
use Dana\Env;
use Dana\PaymentGateway\v1\Api\PaymentGatewayApi;
use Dana\PaymentGateway\v1\Model\QueryPaymentRequest;
// ... define authentication
$queryPaymentRequest = QueryPaymentRequest();
try {
$result = $apiInstance->queryPayment($queryPaymentRequest);
print_r($result);
} catch (Exception $e) {
echo 'Exception when calling PaymentGatewayApi->queryPayment: ', $e->getMessage(), PHP_EOL;
}
<?php
use Dana\Configuration;
use Dana\Env;
use Dana\PaymentGateway\v1\Api\PaymentGatewayApi;
use Dana\PaymentGateway\v1\Model\CancelOrderRequest;
// ... define authentication
$cancelOrderRequest = CancelOrderRequest();
try {
$result = $apiInstance->cancelOrder($cancelOrderRequest);
print_r($result);
} catch (Exception $e) {
echo 'Exception when calling PaymentGatewayApi->cancelOrder: ', $e->getMessage(), PHP_EOL;
}
<?php
use Dana\Configuration;
use Dana\Env;
use Dana\PaymentGateway\v1\Api\PaymentGatewayApi;
use Dana\PaymentGateway\v1\Model\RefundOrderRequest;
// ... define authentication
$refundOrderRequest = RefundOrderRequest();
try {
$result = $apiInstance->refundOrder($refundOrderRequest);
print_r($result);
} catch (Exception $e) {
echo 'Exception when calling PaymentGatewayApi->refundOrder: ', $e->getMessage(), PHP_EOL;
}
Step 6 : Receive Payment Outcome via Finish Notify API
After a successful payment:
- The user will be redirected to your specified Redirect URL, which you can configure using the
urlParams
parameter in the Create Order API API request.
- DANA will send payment notifications to your Notification URL via the Finish Notify API. Configure your notification endpoint with the ASPI-mandated path format:
/v1.0/debit/notify
.
Construction
public function __construct(?string $publicKey = null, ?string $publicKeyPath = null)
Request
Parameter | Type | Remarks |
---|---|---|
publicKey | string | The DANA gateway's public key as a PEM formatted string. This is used if publicKeyPath is not provided or is empty |
publicKeyPath | string | The file path to the DANA gateway's public key PEM file. If provided, this will be prioritized over the publicKey string |
- Throws:
\InvalidArgumentException
if neither publicKey nor publicKeyPath is provided or if the public key cannot be loaded.
Method
public function parseWebhook(string $httpMethod, string $relativePathURL, array $headers, string $body): \Dana\Webhook\v1\Model\FinishNotifyRequest
Request
Parameter | Type | Remarks |
---|---|---|
httpMethod | string | The HTTP method of the incoming webhook request e.g., POST |
relativePathURL | string | The relative URL path of the webhook endpoint that received the notification e.g /v1.0/debit/notify |
headers | array | An array containing the HTTP request headers. This map must include X-SIGNATURE and X-TIMESTAMP headers provided by DANA for signature verification |
body | string | The raw JSON string payload from the webhook request body |
- Return: An instance of
\Dana\Webhook\v1\Model\FinishNotifyRequest
containing the parsed and verified webhook data. - Throws:
\InvalidArgumentException
if required parameters are missing or\RuntimeException
if signature verification fails.
Security Notes
- Always use the official public key provided by DANA for webhook verification. Store and load it securely.
- The
ParseWebhook
method handles both JSON parsing and cryptographic signature verification. If it returns an error, the payload should not be trusted.
<?php
use Dana\Configuration;
use Dana\Webhook\WebhookParser;
// Initialize the WebhookParser
// You can provide the public key directly as a string or via a file path.
// The parser will prioritize publicKeyPath if both are provided.
// Option 1: Provide public key as a string
$danaPublicKey = getenv('DANA_PUBLIC_KEY');
$parser = new WebhookParser($danaPublicKey);
// Option 2: Provide path to public key file
// $danaPublicKeyPath = getenv('DANA_PUBLIC_KEY_PATH'); // e.g., "/path/to/your/dana_public_key.pem"
// $parser = new WebhookParser(null, $danaPublicKeyPath);
// Get the request data
$httpMethod = $_SERVER['REQUEST_METHOD'];
$relativePathUrl = '/v1.0/debit/notify'; // This should match the path DANA sends the webhook to
// Get headers - getallheaders() is the standard way in PHP
$headers = getallheaders();
// For frameworks that don't support getallheaders(), you can use:
// $headers = [];
// foreach ($_SERVER as $name => $value) {
// if (substr($name, 0, 5) === 'HTTP_') {
// $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
// }
// }
// Get the raw request body as a JSON string
$webhookBodyStr = file_get_contents('php://input');
// If you need to access the decoded data before passing to the parser
// (Not required for parseWebhook which expects the raw string)
// $jsonData = json_decode($webhookBodyStr, true);
// if (json_last_error() !== JSON_ERROR_NONE) {
// throw new \RuntimeException('Invalid JSON in webhook payload: ' . json_last_error_msg());
// }
// echo "Request data: " . print_r($jsonData, true);
try {
// Parse and verify the webhook
$parsedData = $parser->parseWebhook(
$httpMethod,
$relativePathUrl,
$headers,
$webhookBodyStr
);
// If we reach here, the webhook was parsed and verified successfully
echo "Webhook verified successfully!\n";
echo "Original Partner Reference No: " . $parsedData->getOriginalPartnerReferenceNo() . "\n";
echo "Amount: " . $parsedData->getAmount()->getValue() . " " . $parsedData->getAmount()->getCurrency() . "\n";
echo "Status: " . $parsedData->getLatestTransactionStatus() . "\n";
// Access additional information if available
if ($parsedData->getAdditionalInfo() && $parsedData->getAdditionalInfo()->getPaymentInfo()) {
$paymentInfo = $parsedData->getAdditionalInfo()->getPaymentInfo();
$payOptions = $paymentInfo->getPayOptionInfos();
foreach ($payOptions as $payOption) {
echo "Payment Method: " . $payOption->getPayMethod() . "\n";
if ($payOption->getPayOption()) {
echo "Payment Option: " . $payOption->getPayOption() . "\n";
}
}
}
} catch (\Exception $e) {
// If verification fails, do not trust the payload
error_log("Webhook verification failed: " . $e->getMessage());
// Respond with an error
header('Content-Type: application/json');
http_response_code(400);
echo json_encode([
'response_code' => '96',
'response_message' => 'System Error'
]);
}
For detailed example, please refer to the following resource: Example Webhook.
Example of a successful payment webhook payload:
Content-Type: application/json
X-TIMESTAMP: 2024-12-23T09:10:11+07:00
{
"responseCode": "2005400", // Refer to response code list
"responseMessage": "Successful", // Refer to response code list
"referenceNo": "2020102977770000000009", // Transaction identifier on DANA system
"partnerReferenceNo": "2020102900000000000001", // Transaction identifier on partner system
"webRedirectUrl": "https://pjsp.com/universal?bizNo=REF993883&..."
...
}
Additional Enum Configuration
The library provides several enums (enumerations) to represent a fixed set of constant values, ensuring consistency and reducing errors during integration.
// Importing an enum class
use Dana\PaymentGateway\v1\Enum\TerminalType;
// Using enum constants
$model->setTerminalType(TerminalType::APP);
// Using enum values directly as strings
$model->setTerminalType('APP');
The following enums are available in the Library DANA Payment Gateway:
- ActorType
- OrderTerminalType
- PayMethod
- PayOption
- SourcePlatform
- TerminalType
- Type
Step 7 : Automated UAT Testing Suite
To verify your integration, run our automated test suite. It takes under 2 minutes to tests your integration with mandated test scenarios. Check out the Github repo for more instructions
Step 8 : Apply for Live Payment
As part of regulatory compliance, merchants are required to submit UAT testing documents to meet Bank Indonesia's requirements. After completing sandbox testing, follow these steps to move to production:
Generate production keys
Create your production private and public keys, follow this instruction: Authentication - Production Credential.Confirm UAT testing logs
Confirm that you have completed all testing scenarios from our Merchant Portal.Fill go-live submission form
Follow the instructions inside our Merchant Portal to apply for production credentials. We will process your application in 1-2 days.Obtain production credentials
Once approved, you will receive your production credentials such as: Merchant ID, Client ID known as X-PARTNER-ID, and Client Secret.
Testing in production environment
Configure production environment
Switch your application settings from sandbox to production environment by updating the API endpoints and credentials.Test using production credentials
Conduct the same testing scenarios as sandbox testing, using your production credentials.UAT production sign-off
Once testing is complete, DANA will prepare the UAT Production Sign Off document in the Merchant Portal. Both merchant and DANA representatives must sign this document to formally approve the integration.Go-live
After receiving all approvals, your DANA integration will be activated and ready for live payments from your customers.
Ready to submit testing documents?
Access our merchant portal for detailed guide to start receiving live payments
- Added additional information section for all APIs
- Removed BusinessScenario Enum
- Create Order API: Updated Service Code
- Create Order API: Updated URL
- Create Order API: Updated Idempotent Key and Rules
- Create Order API: Updated Condition webRedirectUrl on Response section
- Create Order API: Added new request sample programming language
- Create Order API: Updated Request and Response Sample
- Create Order API: Updated Response Code
- Query Payment, Cancel Order, and Refund Order API: Updated URL
- Query Payment, Cancel Order, and Refund Order API: Removed additionalInfo.businessScenario on Request section
- Query Payment, Cancel Order, and Refund Order API: Updated Request Sample
- Updated value of ``REDIRECTION`` become ``REDIRECT`` on ScenarioEnum
- Updated Environment URL for Sandbox on Specification section for all APIs
- Updated Request Sample on URL Section for all APIs
- Added Environment URL on Specification section for all APIs
- Added new programming language (Go, Java, JavaScript, Python, Ruby) on Request Sample for all APIs
- Initial Version