How to integrate Indonesia Payment Service with PHP?
Guide to Integrating Indonesian Payment Services with PHP
There are several popular local payment methods available in the Indonesian market. Below are the steps and considerations for integrating these services using PHP:
Main Indonesia Payment Options
- OVO
- DANA
- LinkAja
- GoPay (now GoTo Financial)
- bank transfer (Mandiri, BCA, BRI, etc.)
- Convenience Store Payments (Alfamart, Indomaret)
Common Integration Steps
1. Selection of payment gateway providers
Consider the following service providers:
- Midtrans (most popular)
- Xendit
- Doku
- iPay88
2. PHP integration example (Midtrans as an example)
// Install Midtrans PHP library: composer require midtrans/midtrans-php
require_once(dirname(__FILE__) . '/vendor/autoload.php');
// Set your Merchant Server Key
\Midtrans\Config::$serverKey = 'YOUR_SERVER_KEY';
// Set to Development/Sandbox Environment (default). Set to true for Production Environment.
\Midtrans\Config::$isProduction = false;
// Enable sanitization
\Midtrans\Config::$isSanitised = true;
// Enable idempotency-key to prevent duplicate transaction
\Midtrans\Config::$isIdempotencyKeyEnabled = true;
$params = [
'transaction_details' => [
'order_id' => rand(),
'gross_amount' => 100000,
],
'payment_type' => 'gopay', // or other payment methods like bank_transfer, ewallet etc.
'customer_details' => [
'first_name' => "John",
'last_name' => "Doe",
'email' => "[email protected]",
// Indonesian phone number format required (+62)
// Example: +6281234567890 - remove the first zero from mobile number and add +62 prefix
// Important for OTP verification on some payment methods like GoPay/DANA/etc.
//
API Call Example (Xendit)
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api.xendit.co/v2/invoices");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($ch, CURLOPT_HEADER, FALSE);
curl_setopt($ch,CURLOPT_POSTFIELDS,'{
"external_id": "demo_147580196270",
"amount":10000.
"description": "Invoice Demo #123".
/* For Indonesian payments */
/* For OVO */
/*"payer_email": "[email protected]",*/
});
/* Add headers */
/* Execute request */
/* Handle response */
Webhook processing (using Xendit as an example)
if($_SERVER['REQUEST_METHOD'] === POST){
$callbackToken= $_SERVER['HTTP_X_CALLBACK_TOKEN'];
if ($callbackToken ! == YOUR_CALLBACK_TOKEN){
http_response_code(403); exit(); }
$json= file_get_contents('php://input');
$data= json_decode($json);
switch ($data->status) {
case PAID: processPaidInvoice(); break;
case EXPIRED: handleExpiredPayment(); break;
}
} else { http_response_code(405); }
Laravel Package Recommendations
For Laravel developers:
composer require veritrans/veritrans-laravel # MidTrans official package
composer require xendit/xendit-laravel # XendIT official package
or the generic Guzzle HTTP client implementation.
We hope this guide has been helpful! Choose the right payment method and API provider for your specific business needs.
Continue PHP Integration Indonesia Payment Service Guide
3. Special requirements for dealing with different modes of payment
OVO Payment Integration
// Example of OVO settings in Midtrans
$params = [
'payment_type' => 'ewallet',
'ewallet' => [
'channel_code' => 'ovo', // OVO's channel code
'channel_properties' => [
// Must use Indonesian mobile phone number format (+628...)
// Note: +62 replaces 0, e.g. 08123456789 → +628123456789.
'mobile_number' => '+6281234567890'
]
],
// ... Other transaction parameters ...
];
try {
$response = \Midtrans\CoreApi::charge($params);
} catch (\Exception $e) {
echo $e->getMessage();
}
DANA Payment Integration Considerations
- Merchant registration required: You need to register for the API key on the DANA Merchant Platform first.
- callback validation: Must implement DANA's callback URL to verify payment status
// DANA Callback Handling Example (Simplified)
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$rawData = file_get_contents('php://input');
if (!empty($rawData)) {
try {
require_once('dana_sdk.php'); // DANA official SDK
$danaClient = new DanaClient([
'clientId' => YOUR_CLIENT_ID,
'secretKey' => YOUR_SECRET_KEY,
// ...
]);
if ($danaClient->validateCallback($rawData)) {
http_response_code(200).
/*
* :: Business logic.
* :: - Update order status to paid
* :: - Follow-up operations such as shipment or provision of services
*/
} else { throw new Exception("Invalid callback signature"); }
} catch (Exception $e) { error_log($e); http_response_code(400); }
}
}
Special Configurations for GoPay/GoTo Financial
Since GoPay is now part of the GoTo ecosystem.
-
sandbox testing:
\Midtrans\Config::$isProduction = false;
\Midtrans\Config::$serverKey = "SB-Mid-server-..." ;
-
production environment:
\Midtrans\Config::$isProduction = true;
/* GoPay may require additional qualifications */
-
User mobile phone number format(Important): PHP preprocessor examples.
function formatIndonesianPhone(string $phone): string
{
/*
* :: Convert local formats like.
* - "08123456789" → "+628123456789"
*/
return preg_replace('/^0/', '+62', ltrim($phone));
}
$customerPhone= formatIndonesianPhone($_POST['phone']);
/* Then use in payment params */
$params['customer_details']['phone'] = validatedNumber;
Alfamart/Indomaret convenience store payments
Applicable to unbanked user groups. The core process is to generate a payment code → users scan the code offline to complete the transaction.
// Midtrans Convenience Store Payment Request Sample Snippet
$convenienceStoreParams= [
"store" => "alfamart", // or indomaret
];
$response= \Midtrans\CoreApi::charge([
... Regular order parameters ... ,
"payment_type"=>"cstore".
"cstore"=> convenienceStoreParams,
]);
/* Response will contain the payment code and expiry time for printing */
echo json_encode([
payment_code=> response->payment_code,
]);
The next thing you may want to know:
- Best practices for Webhook security validation (signature verification, etc.)
- An Elegant Implementation of Laravel's Framework-Specific Approach (Service Provider Design)
- Rupiah Currency Formatting and Decimal Place Handling PHP Tips