Add remaining project files (exclude ignored folders)
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
image: bradrydzewski/php:5.5
|
||||
script:
|
||||
- echo "Hello World"
|
||||
notify:
|
||||
email:
|
||||
recipients:
|
||||
- radityo.shaddiqa@veritrans.co.id
|
||||
@@ -0,0 +1,5 @@
|
||||
composer.lock
|
||||
composer.phar
|
||||
vendor/
|
||||
.idea
|
||||
.DS_STORE
|
||||
@@ -0,0 +1,12 @@
|
||||
language: php
|
||||
php:
|
||||
- 7.4
|
||||
- 7.2
|
||||
- 7.0
|
||||
- 5.6
|
||||
# - 5.5 # PHP version too old
|
||||
# - 5.4 # PHP version too old
|
||||
# - hhvm # HHVM officially drop PHP support
|
||||
|
||||
before_script:
|
||||
- composer install
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Midtrans
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
/**
|
||||
* Check PHP version.
|
||||
*/
|
||||
if (version_compare(PHP_VERSION, '5.4', '<')) {
|
||||
throw new Exception('PHP version >= 5.4 required');
|
||||
}
|
||||
|
||||
// Check PHP Curl & json decode capabilities.
|
||||
if (!function_exists('curl_init') || !function_exists('curl_exec')) {
|
||||
throw new Exception('Midtrans needs the CURL PHP extension.');
|
||||
}
|
||||
if (!function_exists('json_decode')) {
|
||||
throw new Exception('Midtrans needs the JSON PHP extension.');
|
||||
}
|
||||
|
||||
// Configurations
|
||||
require_once 'Midtrans/Config.php';
|
||||
|
||||
// Midtrans API Resources
|
||||
require_once 'Midtrans/Transaction.php';
|
||||
|
||||
// Plumbing
|
||||
require_once 'Midtrans/ApiRequestor.php';
|
||||
require_once 'Midtrans/Notification.php';
|
||||
require_once 'Midtrans/CoreApi.php';
|
||||
require_once 'Midtrans/Snap.php';
|
||||
|
||||
// Sanitization
|
||||
require_once 'Midtrans/Sanitizer.php';
|
||||
@@ -0,0 +1,186 @@
|
||||
<?php
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
use Exception;
|
||||
/**
|
||||
* Send request to Midtrans API
|
||||
* Better don't use this class directly, please use CoreApi, Snap, and Transaction instead
|
||||
*/
|
||||
|
||||
class ApiRequestor
|
||||
{
|
||||
|
||||
/**
|
||||
* Send GET request
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $server_key
|
||||
* @param mixed[] $data_hash
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function get($url, $server_key, $data_hash)
|
||||
{
|
||||
return self::remoteCall($url, $server_key, $data_hash, 'GET');
|
||||
}
|
||||
|
||||
/**
|
||||
* Send POST request
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $server_key
|
||||
* @param mixed[] $data_hash
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function post($url, $server_key, $data_hash)
|
||||
{
|
||||
return self::remoteCall($url, $server_key, $data_hash, 'POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* Send PATCH request
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $server_key
|
||||
* @param mixed[] $data_hash
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function patch($url, $server_key, $data_hash)
|
||||
{
|
||||
return self::remoteCall($url, $server_key, $data_hash, 'PATCH');
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually send request to API server
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $server_key
|
||||
* @param mixed[] $data_hash
|
||||
* @param bool $post
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function remoteCall($url, $server_key, $data_hash, $method)
|
||||
{
|
||||
$ch = curl_init();
|
||||
|
||||
if (!$server_key) {
|
||||
throw new Exception(
|
||||
'The ServerKey/ClientKey is null, You need to set the server-key from Config. Please double-check Config and ServerKey key. ' .
|
||||
'You can check from the Midtrans Dashboard. ' .
|
||||
'See https://docs.midtrans.com/en/midtrans-account/overview?id=retrieving-api-access-keys ' .
|
||||
'for the details or contact support at support@midtrans.com if you have any questions.'
|
||||
);
|
||||
} else {
|
||||
if ($server_key == "") {
|
||||
throw new Exception(
|
||||
'The ServerKey/ClientKey is invalid, as it is an empty string. Please double-check your ServerKey key. ' .
|
||||
'You can check from the Midtrans Dashboard. ' .
|
||||
'See https://docs.midtrans.com/en/midtrans-account/overview?id=retrieving-api-access-keys ' .
|
||||
'for the details or contact support at support@midtrans.com if you have any questions.'
|
||||
);
|
||||
} elseif (preg_match('/\s/',$server_key)) {
|
||||
throw new Exception(
|
||||
'The ServerKey/ClientKey is contains white-space. Please double-check your API key. Please double-check your ServerKey key. ' .
|
||||
'You can check from the Midtrans Dashboard. ' .
|
||||
'See https://docs.midtrans.com/en/midtrans-account/overview?id=retrieving-api-access-keys ' .
|
||||
'for the details or contact support at support@midtrans.com if you have any questions.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$curl_options = array(
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_HTTPHEADER => array(
|
||||
'Content-Type: application/json',
|
||||
'Accept: application/json',
|
||||
'User-Agent: midtrans-php-v2.5.2',
|
||||
'Authorization: Basic ' . base64_encode($server_key . ':')
|
||||
),
|
||||
CURLOPT_RETURNTRANSFER => 1
|
||||
);
|
||||
|
||||
// Set append notification to header
|
||||
if (Config::$appendNotifUrl) Config::$curlOptions[CURLOPT_HTTPHEADER][] = 'X-Append-Notification: ' . Config::$appendNotifUrl;
|
||||
// Set override notification to header
|
||||
if (Config::$overrideNotifUrl) Config::$curlOptions[CURLOPT_HTTPHEADER][] = 'X-Override-Notification: ' . Config::$overrideNotifUrl;
|
||||
// Set payment idempotency-key to header
|
||||
if (Config::$paymentIdempotencyKey) Config::$curlOptions[CURLOPT_HTTPHEADER][] = 'Idempotency-Key: ' . Config::$paymentIdempotencyKey;
|
||||
|
||||
// merging with Config::$curlOptions
|
||||
if (count(Config::$curlOptions)) {
|
||||
// We need to combine headers manually, because it's array and it will no be merged
|
||||
if (Config::$curlOptions[CURLOPT_HTTPHEADER]) {
|
||||
$mergedHeaders = array_merge($curl_options[CURLOPT_HTTPHEADER], Config::$curlOptions[CURLOPT_HTTPHEADER]);
|
||||
$headerOptions = array(CURLOPT_HTTPHEADER => $mergedHeaders);
|
||||
} else {
|
||||
$mergedHeaders = array();
|
||||
$headerOptions = array(CURLOPT_HTTPHEADER => $mergedHeaders);
|
||||
}
|
||||
|
||||
$curl_options = array_replace_recursive($curl_options, Config::$curlOptions, $headerOptions);
|
||||
}
|
||||
|
||||
if ($method != 'GET') {
|
||||
|
||||
if ($data_hash) {
|
||||
$body = json_encode($data_hash);
|
||||
$curl_options[CURLOPT_POSTFIELDS] = $body;
|
||||
} else {
|
||||
$curl_options[CURLOPT_POSTFIELDS] = '';
|
||||
}
|
||||
|
||||
if ($method == 'POST') {
|
||||
$curl_options[CURLOPT_POST] = 1;
|
||||
} elseif ($method == 'PATCH') {
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
|
||||
}
|
||||
}
|
||||
|
||||
curl_setopt_array($ch, $curl_options);
|
||||
|
||||
// For testing purpose
|
||||
if (class_exists('\Midtrans\MT_Tests') && MT_Tests::$stubHttp) {
|
||||
$result = self::processStubed($curl_options, $url, $server_key, $data_hash, $method);
|
||||
} else {
|
||||
$result = curl_exec($ch);
|
||||
// curl_close($ch);
|
||||
}
|
||||
|
||||
|
||||
if ($result === false) {
|
||||
throw new Exception('CURL Error: ' . curl_error($ch), curl_errno($ch));
|
||||
} else {
|
||||
try {
|
||||
$result_array = json_decode($result);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception("API Request Error unable to json_decode API response: ".$result . ' | Request url: '.$url);
|
||||
}
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
if (isset($result_array->status_code) && $result_array->status_code >= 401 && $result_array->status_code != 407) {
|
||||
throw new Exception('Midtrans API is returning API error. HTTP status code: ' . $result_array->status_code . ' API response: ' . $result, $result_array->status_code);
|
||||
} elseif ($httpCode >= 400) {
|
||||
throw new Exception('Midtrans API is returning API error. HTTP status code: ' . $httpCode . ' API response: ' . $result, $httpCode);
|
||||
} else {
|
||||
return $result_array;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function processStubed($curl, $url, $server_key, $data_hash, $method)
|
||||
{
|
||||
MT_Tests::$lastHttpRequest = array(
|
||||
"url" => $url,
|
||||
"server_key" => $server_key,
|
||||
"data_hash" => $data_hash,
|
||||
$method => $method,
|
||||
"curl" => $curl
|
||||
);
|
||||
|
||||
return MT_Tests::$stubHttpResponse;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
/**
|
||||
* Midtrans Configuration
|
||||
*/
|
||||
class Config
|
||||
{
|
||||
|
||||
/**
|
||||
* Your merchant's server key
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
public static $serverKey;
|
||||
/**
|
||||
* Your merchant's client key
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
public static $clientKey;
|
||||
/**
|
||||
* True for production
|
||||
* false for sandbox mode
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
public static $isProduction = false;
|
||||
/**
|
||||
* Set it true to enable 3D Secure by default
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
public static $is3ds = false;
|
||||
/**
|
||||
* Set Append URL notification
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
public static $appendNotifUrl;
|
||||
/**
|
||||
* Set Override URL notification
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
public static $overrideNotifUrl;
|
||||
/**
|
||||
* Set Payment IdempotencyKey
|
||||
* for details (http://api-docs.midtrans.com/#idempotent-requests)
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
public static $paymentIdempotencyKey;
|
||||
/**
|
||||
* Enable request params sanitizer (validate and modify charge request params).
|
||||
* See Midtrans_Sanitizer for more details
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
public static $isSanitized = false;
|
||||
/**
|
||||
* Default options for every request
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
public static $curlOptions = array();
|
||||
|
||||
const SANDBOX_BASE_URL = 'https://api.sandbox.midtrans.com';
|
||||
const PRODUCTION_BASE_URL = 'https://api.midtrans.com';
|
||||
const SNAP_SANDBOX_BASE_URL = 'https://app.sandbox.midtrans.com/snap/v1';
|
||||
const SNAP_PRODUCTION_BASE_URL = 'https://app.midtrans.com/snap/v1';
|
||||
|
||||
/**
|
||||
* Get baseUrl
|
||||
*
|
||||
* @return string Midtrans API URL, depends on $isProduction
|
||||
*/
|
||||
public static function getBaseUrl()
|
||||
{
|
||||
return Config::$isProduction ?
|
||||
Config::PRODUCTION_BASE_URL : Config::SANDBOX_BASE_URL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get snapBaseUrl
|
||||
*
|
||||
* @return string Snap API URL, depends on $isProduction
|
||||
*/
|
||||
public static function getSnapBaseUrl()
|
||||
{
|
||||
return Config::$isProduction ?
|
||||
Config::SNAP_PRODUCTION_BASE_URL : Config::SNAP_SANDBOX_BASE_URL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
<?php
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Provide charge and capture functions for Core API
|
||||
*/
|
||||
class CoreApi
|
||||
{
|
||||
/**
|
||||
* Create transaction.
|
||||
*
|
||||
* @param mixed[] $params Transaction options
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function charge($params)
|
||||
{
|
||||
$payloads = array(
|
||||
'payment_type' => 'credit_card'
|
||||
);
|
||||
|
||||
if (isset($params['item_details'])) {
|
||||
$gross_amount = 0;
|
||||
foreach ($params['item_details'] as $item) {
|
||||
$gross_amount += $item['quantity'] * $item['price'];
|
||||
}
|
||||
$payloads['transaction_details']['gross_amount'] = $gross_amount;
|
||||
}
|
||||
|
||||
$payloads = array_replace_recursive($payloads, $params);
|
||||
|
||||
if (Config::$isSanitized) {
|
||||
Sanitizer::jsonRequest($payloads);
|
||||
}
|
||||
|
||||
return ApiRequestor::post(
|
||||
Config::getBaseUrl() . '/v2/charge',
|
||||
Config::$serverKey,
|
||||
$payloads
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Capture pre-authorized transaction
|
||||
*
|
||||
* @param string $param Order ID or transaction ID, that you want to capture
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function capture($param)
|
||||
{
|
||||
$payloads = array(
|
||||
'transaction_id' => $param,
|
||||
);
|
||||
|
||||
return ApiRequestor::post(
|
||||
Config::getBaseUrl() . '/v2/capture',
|
||||
Config::$serverKey,
|
||||
$payloads
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do `/v2/card/register` API request to Core API
|
||||
*
|
||||
* @param $cardNumber
|
||||
* @param $expMoth
|
||||
* @param $expYear
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function cardRegister($cardNumber, $expMoth, $expYear)
|
||||
{
|
||||
$path = "/card/register?card_number=" . $cardNumber
|
||||
. "&card_exp_month=" . $expMoth
|
||||
. "&card_exp_year=" . $expYear
|
||||
. "&client_key=" . Config::$clientKey;
|
||||
|
||||
return ApiRequestor::get(
|
||||
Config::getBaseUrl() . "/v2" . $path,
|
||||
Config::$clientKey,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do `/v2/token` API request to Core API
|
||||
*
|
||||
* @param $cardNumber
|
||||
* @param $expMoth
|
||||
* @param $expYear
|
||||
* @param $cvv
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function cardToken($cardNumber, $expMoth, $expYear, $cvv)
|
||||
{
|
||||
$path = "/token?card_number=" . $cardNumber
|
||||
. "&card_exp_month=" . $expMoth
|
||||
. "&card_exp_year=" . $expYear
|
||||
. "&card_cvv=" . $cvv
|
||||
. "&client_key=" . Config::$clientKey;
|
||||
|
||||
return ApiRequestor::get(
|
||||
Config::getBaseUrl() . "/v2" . $path,
|
||||
Config::$clientKey,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do `/v2/point_inquiry/<tokenId>` API request to Core API
|
||||
*
|
||||
* @param string tokenId - tokenId of credit card (more params detail refer to: https://api-docs.midtrans.com)
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function cardPointInquiry($tokenId)
|
||||
{
|
||||
return ApiRequestor::get(
|
||||
Config::getBaseUrl() . '/v2/point_inquiry/' . $tokenId,
|
||||
Config::$serverKey,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create `/v2/pay/account` API request to Core API
|
||||
*
|
||||
* @param string create pay account request (more params detail refer to: https://api-docs.midtrans.com/#create-pay-account)
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function linkPaymentAccount($param)
|
||||
{
|
||||
return ApiRequestor::post(
|
||||
Config::getBaseUrl() . '/v2/pay/account',
|
||||
Config::$serverKey,
|
||||
$param
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do `/v2/pay/account/<accountId>` API request to Core API
|
||||
*
|
||||
* @param string accountId (more params detail refer to: https://api-docs.midtrans.com/#get-pay-account)
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getPaymentAccount($accountId)
|
||||
{
|
||||
return ApiRequestor::get(
|
||||
Config::getBaseUrl() . '/v2/pay/account/' . $accountId,
|
||||
Config::$serverKey,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbind `/v2/pay/account/<accountId>/unbind` API request to Core API
|
||||
*
|
||||
* @param string accountId (more params detail refer to: https://api-docs.midtrans.com/#unbind-pay-account)
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function unlinkPaymentAccount($accountId)
|
||||
{
|
||||
return ApiRequestor::post(
|
||||
Config::getBaseUrl() . '/v2/pay/account/' . $accountId . '/unbind',
|
||||
Config::$serverKey,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create `/v1/subscription` API request to Core API
|
||||
*
|
||||
* @param string create subscription request (more params detail refer to: https://api-docs.midtrans.com/#create-subscription)
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function createSubscription($param)
|
||||
{
|
||||
return ApiRequestor::post(
|
||||
Config::getBaseUrl() . '/v1/subscriptions',
|
||||
Config::$serverKey,
|
||||
$param
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do `/v1/subscription/<subscription_id>` API request to Core API
|
||||
*
|
||||
* @param string get subscription request (more params detail refer to: https://api-docs.midtrans.com/#get-subscription)
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getSubscription($SubscriptionId)
|
||||
{
|
||||
return ApiRequestor::get(
|
||||
Config::getBaseUrl() . '/v1/subscriptions/' . $SubscriptionId,
|
||||
Config::$serverKey,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do disable `/v1/subscription/<subscription_id>/disable` API request to Core API
|
||||
*
|
||||
* @param string disable subscription request (more params detail refer to: https://api-docs.midtrans.com/#disable-subscription)
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function disableSubscription($SubscriptionId)
|
||||
{
|
||||
return ApiRequestor::post(
|
||||
Config::getBaseUrl() . '/v1/subscriptions/' . $SubscriptionId . '/disable',
|
||||
Config::$serverKey,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do enable `/v1/subscription/<subscription_id>/enable` API request to Core API
|
||||
*
|
||||
* @param string enable subscription request (more params detail refer to: https://api-docs.midtrans.com/#enable-subscription)
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function enableSubscription($SubscriptionId)
|
||||
{
|
||||
return ApiRequestor::post(
|
||||
Config::getBaseUrl() . '/v1/subscriptions/' . $SubscriptionId . '/enable',
|
||||
Config::$serverKey,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do update subscription `/v1/subscription/<subscription_id>` API request to Core API
|
||||
*
|
||||
* @param string update subscription request (more params detail refer to: https://api-docs.midtrans.com/#update-subscription)
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function updateSubscription($SubscriptionId, $param)
|
||||
{
|
||||
return ApiRequestor::patch(
|
||||
Config::getBaseUrl() . '/v1/subscriptions/' . $SubscriptionId,
|
||||
Config::$serverKey,
|
||||
$param
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
/**
|
||||
* Read raw post input and parse as JSON. Provide getters for fields in notification object
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```php
|
||||
*
|
||||
* namespace Midtrans;
|
||||
*
|
||||
* $notif = new Notification();
|
||||
* echo $notif->order_id;
|
||||
* echo $notif->transaction_status;
|
||||
* ```
|
||||
*/
|
||||
class Notification
|
||||
{
|
||||
private $response;
|
||||
|
||||
public function __construct($input_source = "php://input")
|
||||
{
|
||||
$raw_notification = json_decode(file_get_contents($input_source), true);
|
||||
$status_response = Transaction::status($raw_notification['transaction_id']);
|
||||
$this->response = $status_response;
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
if (isset($this->response->$name)) {
|
||||
return $this->response->$name;
|
||||
}
|
||||
}
|
||||
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
/**
|
||||
* Request params filters.
|
||||
*
|
||||
* It truncate fields that have length limit, remove not allowed characters from other fields
|
||||
*
|
||||
* This feature is optional, you can control it with Config::$isSanitized (default: false)
|
||||
*/
|
||||
class Sanitizer
|
||||
{
|
||||
private $filters;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->filters = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates and modify data
|
||||
*
|
||||
* @param mixed[] $json
|
||||
*/
|
||||
public static function jsonRequest(&$json)
|
||||
{
|
||||
$keys = array('item_details', 'customer_details');
|
||||
foreach ($keys as $key) {
|
||||
if (!isset($json[$key])) continue;
|
||||
$camel = static::upperCamelize($key);
|
||||
$function = "field$camel";
|
||||
static::$function($json[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
private static function fieldItemDetails(&$items)
|
||||
{
|
||||
foreach ($items as &$item) {
|
||||
$id = new self;
|
||||
$item['id'] = $id
|
||||
->maxLength(50)
|
||||
->apply($item['id']);
|
||||
$name = new self;
|
||||
$item['name'] = $name
|
||||
->maxLength(50)
|
||||
->apply($item['name']);
|
||||
}
|
||||
}
|
||||
|
||||
private static function fieldCustomerDetails(&$field)
|
||||
{
|
||||
if (isset($field['first_name'])) {
|
||||
$first_name = new self;
|
||||
$field['first_name'] = $first_name->maxLength(255)->apply($field['first_name']);
|
||||
}
|
||||
|
||||
if (isset($field['last_name'])) {
|
||||
$last_name = new self;
|
||||
$field['last_name'] = $last_name->maxLength(255)->apply($field['last_name']);
|
||||
}
|
||||
|
||||
if (isset($field['email'])) {
|
||||
$email = new self;
|
||||
$field['email'] = $email->maxLength(255)->apply($field['email']);
|
||||
}
|
||||
|
||||
if (isset($field['phone'])) {
|
||||
$phone = new self;
|
||||
$field['phone'] = $phone->maxLength(255)->apply($field['phone']);
|
||||
}
|
||||
|
||||
if (!empty($field['billing_address']) || !empty($field['shipping_address'])) {
|
||||
$keys = array('billing_address', 'shipping_address');
|
||||
foreach ($keys as $key) {
|
||||
if (!isset($field[$key])) continue;
|
||||
|
||||
$camel = static::upperCamelize($key);
|
||||
$function = "field$camel";
|
||||
static::$function($field[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static function fieldBillingAddress(&$field)
|
||||
{
|
||||
$fields = array(
|
||||
'first_name' => 255,
|
||||
'last_name' => 255,
|
||||
'address' => 255,
|
||||
'city' => 255,
|
||||
'country_code' => 3
|
||||
);
|
||||
|
||||
foreach ($fields as $key => $value) {
|
||||
if (isset($field[$key])) {
|
||||
$self = new self;
|
||||
$field[$key] = $self
|
||||
->maxLength($value)
|
||||
->apply($field[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($field['postal_code'])) {
|
||||
$postal_code = new self;
|
||||
$field['postal_code'] = $postal_code
|
||||
->whitelist('A-Za-z0-9\\- ')
|
||||
->maxLength(10)
|
||||
->apply($field['postal_code']);
|
||||
}
|
||||
if (isset($field['phone'])) {
|
||||
static::fieldPhone($field['phone']);
|
||||
}
|
||||
}
|
||||
|
||||
private static function fieldShippingAddress(&$field)
|
||||
{
|
||||
static::fieldBillingAddress($field);
|
||||
}
|
||||
|
||||
private static function fieldPhone(&$field)
|
||||
{
|
||||
$plus = substr($field, 0, 1) === '+';
|
||||
$self = new self;
|
||||
$field = $self
|
||||
->whitelist('\\d\\-\\(\\) ')
|
||||
->maxLength(19)
|
||||
->apply($field);
|
||||
|
||||
if ($plus) $field = '+' . $field;
|
||||
$self = new self;
|
||||
$field = $self
|
||||
->maxLength(19)
|
||||
->apply($field);
|
||||
}
|
||||
|
||||
private function maxLength($length)
|
||||
{
|
||||
$this->filters[] = function ($input) use ($length) {
|
||||
return substr($input, 0, $length);
|
||||
};
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function whitelist($regex)
|
||||
{
|
||||
$this->filters[] = function ($input) use ($regex) {
|
||||
return preg_replace("/[^$regex]/", '', $input);
|
||||
};
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function apply($input)
|
||||
{
|
||||
foreach ($this->filters as $filter) {
|
||||
$input = call_user_func($filter, $input);
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
|
||||
private static function upperCamelize($string)
|
||||
{
|
||||
return str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Create Snap payment page and return snap token
|
||||
*/
|
||||
class Snap
|
||||
{
|
||||
/**
|
||||
* Create Snap payment page
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```php
|
||||
*
|
||||
* namespace Midtrans;
|
||||
*
|
||||
* $params = array(
|
||||
* 'transaction_details' => array(
|
||||
* 'order_id' => rand(),
|
||||
* 'gross_amount' => 10000,
|
||||
* )
|
||||
* );
|
||||
* $paymentUrl = Snap::getSnapToken($params);
|
||||
* ```
|
||||
*
|
||||
* @param array $params Payment options
|
||||
* @return string Snap token.
|
||||
* @throws Exception curl error or midtrans error
|
||||
*/
|
||||
public static function getSnapToken($params)
|
||||
{
|
||||
return (Snap::createTransaction($params)->token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Snap URL payment
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```php
|
||||
*
|
||||
* namespace Midtrans;
|
||||
*
|
||||
* $params = array(
|
||||
* 'transaction_details' => array(
|
||||
* 'order_id' => rand(),
|
||||
* 'gross_amount' => 10000,
|
||||
* )
|
||||
* );
|
||||
* $paymentUrl = Snap::getSnapUrl($params);
|
||||
* ```
|
||||
*
|
||||
* @param array $params Payment options
|
||||
* @return string Snap redirect url.
|
||||
* @throws Exception curl error or midtrans error
|
||||
*/
|
||||
public static function getSnapUrl($params)
|
||||
{
|
||||
return (Snap::createTransaction($params)->redirect_url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Snap payment page, with this version returning full API response
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```php
|
||||
* $params = array(
|
||||
* 'transaction_details' => array(
|
||||
* 'order_id' => rand(),
|
||||
* 'gross_amount' => 10000,
|
||||
* )
|
||||
* );
|
||||
* $paymentUrl = Snap::getSnapToken($params);
|
||||
* ```
|
||||
*
|
||||
* @param array $params Payment options
|
||||
* @return object Snap response (token and redirect_url).
|
||||
* @throws Exception curl error or midtrans error
|
||||
*/
|
||||
public static function createTransaction($params)
|
||||
{
|
||||
$payloads = array(
|
||||
'credit_card' => array(
|
||||
// 'enabled_payments' => array('credit_card'),
|
||||
'secure' => Config::$is3ds
|
||||
)
|
||||
);
|
||||
|
||||
if (isset($params['item_details'])) {
|
||||
$gross_amount = 0;
|
||||
foreach ($params['item_details'] as $item) {
|
||||
$gross_amount += $item['quantity'] * $item['price'];
|
||||
}
|
||||
$params['transaction_details']['gross_amount'] = $gross_amount;
|
||||
}
|
||||
|
||||
if (Config::$isSanitized) {
|
||||
Sanitizer::jsonRequest($params);
|
||||
}
|
||||
|
||||
$params = array_replace_recursive($payloads, $params);
|
||||
|
||||
return ApiRequestor::post(
|
||||
Config::getSnapBaseUrl() . '/transactions',
|
||||
Config::$serverKey,
|
||||
$params
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
/**
|
||||
* Send request to Snap API
|
||||
* Better don't use this class directly, use Snap
|
||||
* @deprecated this class already deprecated. We will deleted on the next major release. We have been centralized the
|
||||
* requestor via ApiRequestor.php
|
||||
*/
|
||||
|
||||
class SnapApiRequestor
|
||||
{
|
||||
/**
|
||||
* Send GET request
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $server_key
|
||||
* @param mixed[] $data_hash
|
||||
*/
|
||||
public static function get($url, $server_key, $data_hash)
|
||||
{
|
||||
return self::remoteCall($url, $server_key, $data_hash, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send POST request
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $server_key
|
||||
* @param mixed[] $data_hash
|
||||
*/
|
||||
public static function post($url, $server_key, $data_hash)
|
||||
{
|
||||
return self::remoteCall($url, $server_key, $data_hash, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually send request to API server
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $server_key
|
||||
* @param mixed[] $data_hash
|
||||
* @param bool $post
|
||||
*/
|
||||
public static function remoteCall($url, $server_key, $data_hash, $post = true)
|
||||
{
|
||||
$ch = curl_init();
|
||||
|
||||
$curl_options = array(
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_HTTPHEADER => array(
|
||||
'Content-Type: application/json',
|
||||
'Accept: application/json',
|
||||
'Authorization: Basic ' . base64_encode($server_key . ':')
|
||||
),
|
||||
CURLOPT_RETURNTRANSFER => 1,
|
||||
// CURLOPT_CAINFO => dirname(__FILE__) . "/../data/cacert.pem"
|
||||
);
|
||||
|
||||
// merging with Config::$curlOptions
|
||||
if (count(Config::$curlOptions)) {
|
||||
// We need to combine headers manually, because it's array and it will no be merged
|
||||
if (Config::$curlOptions[CURLOPT_HTTPHEADER]) {
|
||||
$mergedHeders = array_merge($curl_options[CURLOPT_HTTPHEADER], Config::$curlOptions[CURLOPT_HTTPHEADER]);
|
||||
$headerOptions = array( CURLOPT_HTTPHEADER => $mergedHeders );
|
||||
} else {
|
||||
$mergedHeders = array();
|
||||
}
|
||||
|
||||
$curl_options = array_replace_recursive($curl_options, Config::$curlOptions, $headerOptions);
|
||||
}
|
||||
|
||||
if ($post) {
|
||||
$curl_options[CURLOPT_POST] = 1;
|
||||
|
||||
if ($data_hash) {
|
||||
$body = json_encode($data_hash);
|
||||
$curl_options[CURLOPT_POSTFIELDS] = $body;
|
||||
} else {
|
||||
$curl_options[CURLOPT_POSTFIELDS] = '';
|
||||
}
|
||||
}
|
||||
|
||||
curl_setopt_array($ch, $curl_options);
|
||||
|
||||
// For testing purpose
|
||||
if (class_exists('\Midtrans\VT_Tests') && VT_Tests::$stubHttp) {
|
||||
$result = self::processStubed($curl_options, $url, $server_key, $data_hash, $post);
|
||||
$info = VT_Tests::$stubHttpStatus;
|
||||
} else {
|
||||
$result = curl_exec($ch);
|
||||
$info = curl_getinfo($ch);
|
||||
// curl_close($ch);
|
||||
}
|
||||
|
||||
if ($result === false) {
|
||||
throw new \Exception('CURL Error: ' . curl_error($ch), curl_errno($ch));
|
||||
} else {
|
||||
try {
|
||||
$result_array = json_decode($result);
|
||||
} catch (\Exception $e) {
|
||||
$message = "API Request Error unable to json_decode API response: ".$result . ' | Request url: '.$url;
|
||||
throw new \Exception($message);
|
||||
}
|
||||
if ($info['http_code'] != 201) {
|
||||
$message = 'Midtrans Error (' . $info['http_code'] . '): '
|
||||
. $result . ' | Request url: '.$url;
|
||||
throw new \Exception($message, $info['http_code']);
|
||||
} else {
|
||||
return $result_array;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function processStubed($curl, $url, $server_key, $data_hash, $post)
|
||||
{
|
||||
VT_Tests::$lastHttpRequest = array(
|
||||
"url" => $url,
|
||||
"server_key" => $server_key,
|
||||
"data_hash" => $data_hash,
|
||||
"post" => $post,
|
||||
"curl" => $curl
|
||||
);
|
||||
|
||||
return VT_Tests::$stubHttpResponse;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
use Exception;
|
||||
/**
|
||||
* API methods to get transaction status, approve and cancel transactions
|
||||
*/
|
||||
class Transaction
|
||||
{
|
||||
|
||||
/**
|
||||
* Retrieve transaction status
|
||||
*
|
||||
* @param string $id Order ID or transaction ID
|
||||
*
|
||||
* @return mixed[]
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function status($id)
|
||||
{
|
||||
return ApiRequestor::get(
|
||||
Config::getBaseUrl() . '/v2/' . $id . '/status',
|
||||
Config::$serverKey,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve B2B transaction status
|
||||
*
|
||||
* @param string $id Order ID or transaction ID
|
||||
*
|
||||
* @return mixed[]
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function statusB2b($id)
|
||||
{
|
||||
return ApiRequestor::get(
|
||||
Config::getBaseUrl() . '/v2/' . $id . '/status/b2b',
|
||||
Config::$serverKey,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve challenge transaction
|
||||
*
|
||||
* @param string $id Order ID or transaction ID
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function approve($id)
|
||||
{
|
||||
return ApiRequestor::post(
|
||||
Config::getBaseUrl() . '/v2/' . $id . '/approve',
|
||||
Config::$serverKey,
|
||||
false
|
||||
)->status_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel transaction before it's settled
|
||||
*
|
||||
* @param string $id Order ID or transaction ID
|
||||
*
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function cancel($id)
|
||||
{
|
||||
return ApiRequestor::post(
|
||||
Config::getBaseUrl() . '/v2/' . $id . '/cancel',
|
||||
Config::$serverKey,
|
||||
false
|
||||
)->status_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expire transaction before it's setteled
|
||||
*
|
||||
* @param string $id Order ID or transaction ID
|
||||
*
|
||||
* @return mixed[]
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function expire($id)
|
||||
{
|
||||
return ApiRequestor::post(
|
||||
Config::getBaseUrl() . '/v2/' . $id . '/expire',
|
||||
Config::$serverKey,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transaction status can be updated into refund
|
||||
* if the customer decides to cancel completed/settlement payment.
|
||||
* The same refund id cannot be reused again.
|
||||
*
|
||||
* @param string $id Order ID or transaction ID
|
||||
*
|
||||
* @param $params
|
||||
* @return mixed[]
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function refund($id, $params)
|
||||
{
|
||||
return ApiRequestor::post(
|
||||
Config::getBaseUrl() . '/v2/' . $id . '/refund',
|
||||
Config::$serverKey,
|
||||
$params
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transaction status can be updated into refund
|
||||
* if the customer decides to cancel completed/settlement payment.
|
||||
* The same refund id cannot be reused again.
|
||||
*
|
||||
* @param string $id Order ID or transaction ID
|
||||
*
|
||||
* @return mixed[]
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function refundDirect($id, $params)
|
||||
{
|
||||
return ApiRequestor::post(
|
||||
Config::getBaseUrl() . '/v2/' . $id . '/refund/online/direct',
|
||||
Config::$serverKey,
|
||||
$params
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deny method can be triggered to immediately deny card payment transaction
|
||||
* in which fraud_status is challenge.
|
||||
*
|
||||
* @param string $id Order ID or transaction ID
|
||||
*
|
||||
* @return mixed[]
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function deny($id)
|
||||
{
|
||||
return ApiRequestor::post(
|
||||
Config::getBaseUrl() . '/v2/' . $id . '/deny',
|
||||
Config::$serverKey,
|
||||
false
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,464 @@
|
||||
Midtrans-PHP
|
||||
===============
|
||||
|
||||
[](https://badge.fury.io/ph/midtrans%2Fmidtrans-php)
|
||||
[](https://packagist.org/packages/midtrans/midtrans-php)
|
||||
[](https://packagist.org/packages/midtrans/midtrans-php)
|
||||
[](https://packagist.org/packages/midtrans/midtrans-php)
|
||||
<!-- [](https://travis-ci.org/midtrans/midtrans-php) -->
|
||||
|
||||
[Midtrans](https://midtrans.com) :heart: PHP!
|
||||
|
||||
This is the Official PHP wrapper/library for Midtrans Payment API, that is compatible with Composer. Visit [https://midtrans.com](https://midtrans.com) for more information about the product and see documentation at [http://docs.midtrans.com](https://docs.midtrans.com) for more technical details.
|
||||
|
||||
## 1. Installation
|
||||
|
||||
### 1.a Composer Installation
|
||||
|
||||
If you are using [Composer](https://getcomposer.org), you can install via composer CLI:
|
||||
|
||||
```
|
||||
composer require midtrans/midtrans-php
|
||||
```
|
||||
|
||||
**or**
|
||||
|
||||
add this require line to your `composer.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"require": {
|
||||
"midtrans/midtrans-php": "2.*"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
and run `composer install` on your terminal.
|
||||
|
||||
> **Note:** If you are using Laravel framework, in [some](https://laracasts.com/discuss/channels/general-discussion/using-non-laravel-composer-package-with-laravel?page=1#reply=461608) [case](https://stackoverflow.com/a/23675376) you also need to run `composer dumpautoload`
|
||||
|
||||
> `/Midtrans` will then be available (auto loaded) as Object in your Laravel project.
|
||||
|
||||
### 1.b Manual Instalation
|
||||
|
||||
If you are not using Composer, you can clone or [download](https://github.com/midtrans/midtrans-php/archive/master.zip) this repository.
|
||||
|
||||
Then you should require/autoload `Midtrans.php` file on your code.
|
||||
|
||||
```php
|
||||
require_once dirname(__FILE__) . '/pathofproject/Midtrans.php';
|
||||
|
||||
// my code goes here
|
||||
```
|
||||
|
||||
## 2. How to Use
|
||||
|
||||
### 2.1 General Settings
|
||||
|
||||
```php
|
||||
// Set your Merchant Server Key
|
||||
\Midtrans\Config::$serverKey = '<your server key>';
|
||||
// Set to Development/Sandbox Environment (default). Set to true for Production Environment (accept real transaction).
|
||||
\Midtrans\Config::$isProduction = false;
|
||||
// Set sanitization on (default)
|
||||
\Midtrans\Config::$isSanitized = true;
|
||||
// Set 3DS transaction for credit card to true
|
||||
\Midtrans\Config::$is3ds = true;
|
||||
```
|
||||
|
||||
#### Override Notification URL
|
||||
|
||||
You can opt to change or add custom notification urls on every transaction. It can be achieved by adding additional HTTP headers into charge request.
|
||||
|
||||
```php
|
||||
// Add new notification url(s) alongside the settings on Midtrans Dashboard Portal (MAP)
|
||||
Config::$appendNotifUrl = "https://example.com/test1,https://example.com/test2";
|
||||
// Use new notification url(s) disregarding the settings on Midtrans Dashboard Portal (MAP)
|
||||
Config::$overrideNotifUrl = "https://example.com/test1";
|
||||
```
|
||||
|
||||
[More details](https://api-docs.midtrans.com/#override-notification-url)
|
||||
|
||||
> **Note:** When both `appendNotifUrl` and `overrideNotifUrl` are used together then only `overrideNotifUrl` will be used.
|
||||
|
||||
> Both header can only receive up to maximum of **3 urls**.
|
||||
|
||||
#### Idempotency-Key
|
||||
You can opt to add idempotency key on charge transaction. It can be achieved by adding additional HTTP headers into charge request.
|
||||
Is a unique value that is put on header on API request. Midtrans API accept Idempotency-Key on header to safely handle retry request
|
||||
without performing the same operation twice. This is helpful for cases where merchant didn't receive the response because of network issue or other unexpected error.
|
||||
|
||||
```php
|
||||
Config::$paymentIdempotencyKey = "Unique-ID";
|
||||
```
|
||||
|
||||
[More details](http://api-docs.midtrans.com/#idempotent-requests)
|
||||
|
||||
### 2.2 Choose Product/Method
|
||||
|
||||
We have [3 different products](https://docs.midtrans.com/en/welcome/index.html) of payment that you can use:
|
||||
- [Snap](#22a-snap) - Customizable payment popup will appear on **your web/app** (no redirection). [doc ref](https://snap-docs.midtrans.com/)
|
||||
- [Snap Redirect](#22b-snap-redirect) - Customer need to be redirected to payment url **hosted by midtrans**. [doc ref](https://snap-docs.midtrans.com/)
|
||||
- [Core API (VT-Direct)](#22c-core-api-vt-direct) - Basic backend implementation, you can customize the frontend embedded on **your web/app** as you like (no redirection). [doc ref](https://api-docs.midtrans.com/)
|
||||
|
||||
Choose one that you think best for your unique needs.
|
||||
|
||||
### 2.2.a Snap
|
||||
|
||||
You can see Snap example [here](examples/snap).
|
||||
|
||||
#### Get Snap Token
|
||||
|
||||
```php
|
||||
$params = array(
|
||||
'transaction_details' => array(
|
||||
'order_id' => rand(),
|
||||
'gross_amount' => 10000,
|
||||
)
|
||||
);
|
||||
|
||||
$snapToken = \Midtrans\Snap::getSnapToken($params);
|
||||
```
|
||||
|
||||
#### Initialize Snap JS when customer click pay button
|
||||
|
||||
```html
|
||||
<html>
|
||||
<body>
|
||||
<button id="pay-button">Pay!</button>
|
||||
<pre><div id="result-json">JSON result will appear here after payment:<br></div></pre>
|
||||
|
||||
<!-- TODO: Remove ".sandbox" from script src URL for production environment. Also input your client key in "data-client-key" -->
|
||||
<script src="https://app.sandbox.midtrans.com/snap/snap.js" data-client-key="<Set your ClientKey here>"></script>
|
||||
<script type="text/javascript">
|
||||
document.getElementById('pay-button').onclick = function(){
|
||||
// SnapToken acquired from previous step
|
||||
snap.pay('<?=$snapToken?>', {
|
||||
// Optional
|
||||
onSuccess: function(result){
|
||||
/* You may add your own js here, this is just example */ document.getElementById('result-json').innerHTML += JSON.stringify(result, null, 2);
|
||||
},
|
||||
// Optional
|
||||
onPending: function(result){
|
||||
/* You may add your own js here, this is just example */ document.getElementById('result-json').innerHTML += JSON.stringify(result, null, 2);
|
||||
},
|
||||
// Optional
|
||||
onError: function(result){
|
||||
/* You may add your own js here, this is just example */ document.getElementById('result-json').innerHTML += JSON.stringify(result, null, 2);
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
#### Implement Notification Handler
|
||||
[Refer to this section](#23-handle-http-notification)
|
||||
|
||||
### 2.2.b Snap Redirect
|
||||
|
||||
You can see some Snap Redirect examples [here](examples/snap-redirect).
|
||||
|
||||
#### Get Redirection URL of a Payment Page
|
||||
|
||||
```php
|
||||
$params = array(
|
||||
'transaction_details' => array(
|
||||
'order_id' => rand(),
|
||||
'gross_amount' => 10000,
|
||||
)
|
||||
);
|
||||
|
||||
try {
|
||||
// Get Snap Payment Page URL
|
||||
$paymentUrl = \Midtrans\Snap::createTransaction($params)->redirect_url;
|
||||
|
||||
// Redirect to Snap Payment Page
|
||||
header('Location: ' . $paymentUrl);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
echo $e->getMessage();
|
||||
}
|
||||
```
|
||||
#### Implement Notification Handler
|
||||
[Refer to this section](#23-handle-http-notification)
|
||||
|
||||
### 2.2.c Core API (VT-Direct)
|
||||
|
||||
You can see some Core API examples [here](examples/core-api).
|
||||
|
||||
#### Set Client Key
|
||||
|
||||
```javascript
|
||||
MidtransNew3ds.clientKey = "<your client key>";
|
||||
```
|
||||
|
||||
#### Checkout Page
|
||||
|
||||
Please refer to [this file](examples/core-api/checkout.php)
|
||||
|
||||
#### Checkout Process
|
||||
|
||||
##### 1. Create Transaction Details
|
||||
|
||||
```php
|
||||
$transaction_details = array(
|
||||
'order_id' => time(),
|
||||
'gross_amount' => 200000
|
||||
);
|
||||
```
|
||||
|
||||
##### 2. Create Item Details, Billing Address, Shipping Address, and Customer Details (Optional)
|
||||
|
||||
```php
|
||||
// Populate items
|
||||
$items = array(
|
||||
array(
|
||||
'id' => 'item1',
|
||||
'price' => 100000,
|
||||
'quantity' => 1,
|
||||
'name' => 'Adidas f50'
|
||||
),
|
||||
array(
|
||||
'id' => 'item2',
|
||||
'price' => 50000,
|
||||
'quantity' => 2,
|
||||
'name' => 'Nike N90'
|
||||
)
|
||||
);
|
||||
|
||||
// Populate customer's billing address
|
||||
$billing_address = array(
|
||||
'first_name' => "Andri",
|
||||
'last_name' => "Setiawan",
|
||||
'address' => "Karet Belakang 15A, Setiabudi.",
|
||||
'city' => "Jakarta",
|
||||
'postal_code' => "51161",
|
||||
'phone' => "081322311801",
|
||||
'country_code' => 'IDN'
|
||||
);
|
||||
|
||||
// Populate customer's shipping address
|
||||
$shipping_address = array(
|
||||
'first_name' => "John",
|
||||
'last_name' => "Watson",
|
||||
'address' => "Bakerstreet 221B.",
|
||||
'city' => "Jakarta",
|
||||
'postal_code' => "51162",
|
||||
'phone' => "081322311801",
|
||||
'country_code' => 'IDN'
|
||||
);
|
||||
|
||||
// Populate customer's info
|
||||
$customer_details = array(
|
||||
'first_name' => "Andri",
|
||||
'last_name' => "Setiawan",
|
||||
'email' => "test@test.com",
|
||||
'phone' => "081322311801",
|
||||
'billing_address' => $billing_address,
|
||||
'shipping_address' => $shipping_address
|
||||
);
|
||||
```
|
||||
|
||||
##### 3. Get Token ID from Checkout Page
|
||||
|
||||
```php
|
||||
// Token ID from checkout page
|
||||
$token_id = $_POST['token_id'];
|
||||
```
|
||||
|
||||
##### 4. Create Transaction Data
|
||||
|
||||
```php
|
||||
// Transaction data to be sent
|
||||
$transaction_data = array(
|
||||
'payment_type' => 'credit_card',
|
||||
'credit_card' => array(
|
||||
'token_id' => $token_id,
|
||||
'authentication'=> true,
|
||||
// 'bank' => 'bni', // optional to set acquiring bank
|
||||
// 'save_token_id' => true // optional for one/two clicks feature
|
||||
),
|
||||
'transaction_details' => $transaction_details,
|
||||
'item_details' => $items,
|
||||
'customer_details' => $customer_details
|
||||
);
|
||||
```
|
||||
|
||||
##### 5. Charge
|
||||
|
||||
```php
|
||||
$response = \Midtrans\CoreApi::charge($transaction_data);
|
||||
```
|
||||
|
||||
|
||||
##### 6. Credit Card 3DS Authentication
|
||||
|
||||
The credit card charge result may contains `redirect_url` for 3DS authentication. 3DS Authentication should be handled on Frontend please refer to [API docs](https://api-docs.midtrans.com/#card-features-3d-secure)
|
||||
|
||||
For full example on Credit Card 3DS transaction refer to:
|
||||
- [Core API examples](/examples/core-api/)
|
||||
|
||||
##### 7. Handle Transaction Status
|
||||
|
||||
```php
|
||||
// Success
|
||||
if($response->transaction_status == 'capture') {
|
||||
echo "<p>Transaksi berhasil.</p>";
|
||||
echo "<p>Status transaksi untuk order id $response->order_id: " .
|
||||
"$response->transaction_status</p>";
|
||||
|
||||
echo "<h3>Detail transaksi:</h3>";
|
||||
echo "<pre>";
|
||||
var_dump($response);
|
||||
echo "</pre>";
|
||||
}
|
||||
// Deny
|
||||
else if($response->transaction_status == 'deny') {
|
||||
echo "<p>Transaksi ditolak.</p>";
|
||||
echo "<p>Status transaksi untuk order id .$response->order_id: " .
|
||||
"$response->transaction_status</p>";
|
||||
|
||||
echo "<h3>Detail transaksi:</h3>";
|
||||
echo "<pre>";
|
||||
var_dump($response);
|
||||
echo "</pre>";
|
||||
}
|
||||
// Challenge
|
||||
else if($response->transaction_status == 'challenge') {
|
||||
echo "<p>Transaksi challenge.</p>";
|
||||
echo "<p>Status transaksi untuk order id $response->order_id: " .
|
||||
"$response->transaction_status</p>";
|
||||
|
||||
echo "<h3>Detail transaksi:</h3>";
|
||||
echo "<pre>";
|
||||
var_dump($response);
|
||||
echo "</pre>";
|
||||
}
|
||||
// Error
|
||||
else {
|
||||
echo "<p>Terjadi kesalahan pada data transaksi yang dikirim.</p>";
|
||||
echo "<p>Status message: [$response->status_code] " .
|
||||
"$response->status_message</p>";
|
||||
|
||||
echo "<pre>";
|
||||
var_dump($response);
|
||||
echo "</pre>";
|
||||
}
|
||||
```
|
||||
#### 8. Implement Notification Handler
|
||||
[Refer to this section](#23-handle-http-notification)
|
||||
|
||||
|
||||
### 2.3 Handle HTTP Notification
|
||||
|
||||
Create separated web endpoint (notification url) to receive HTTP POST notification callback/webhook.
|
||||
HTTP notification will be sent whenever transaction status is changed.
|
||||
Example also available [here](examples/notification-handler.php)
|
||||
|
||||
```php
|
||||
$notif = new \Midtrans\Notification();
|
||||
|
||||
$transaction = $notif->transaction_status;
|
||||
$fraud = $notif->fraud_status;
|
||||
|
||||
error_log("Order ID $notif->order_id: "."transaction status = $transaction, fraud staus = $fraud");
|
||||
|
||||
if ($transaction == 'capture') {
|
||||
if ($fraud == 'challenge') {
|
||||
// TODO Set payment status in merchant's database to 'challenge'
|
||||
}
|
||||
else if ($fraud == 'accept') {
|
||||
// TODO Set payment status in merchant's database to 'success'
|
||||
}
|
||||
}
|
||||
else if ($transaction == 'cancel') {
|
||||
if ($fraud == 'challenge') {
|
||||
// TODO Set payment status in merchant's database to 'failure'
|
||||
}
|
||||
else if ($fraud == 'accept') {
|
||||
// TODO Set payment status in merchant's database to 'failure'
|
||||
}
|
||||
}
|
||||
else if ($transaction == 'deny') {
|
||||
// TODO Set payment status in merchant's database to 'failure'
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 Process Transaction
|
||||
|
||||
#### Get Transaction Status
|
||||
|
||||
```php
|
||||
$status = \Midtrans\Transaction::status($orderId);
|
||||
var_dump($status);
|
||||
```
|
||||
|
||||
#### Approve Transaction
|
||||
If transaction fraud_status == [CHALLENGE](https://support.midtrans.com/hc/en-us/articles/202710750-What-does-CHALLENGE-status-mean-What-should-I-do-if-there-is-a-CHALLENGE-transaction-), you can approve the transaction from Merchant Dashboard, or API :
|
||||
|
||||
```php
|
||||
$approve = \Midtrans\Transaction::approve($orderId);
|
||||
var_dump($approve);
|
||||
```
|
||||
|
||||
#### Cancel Transaction
|
||||
You can Cancel transaction with `fraud_status == CHALLENGE`, or credit card transaction with `transaction_status == CAPTURE` (before it become SETTLEMENT)
|
||||
```php
|
||||
$cancel = \Midtrans\Transaction::cancel($orderId);
|
||||
var_dump($cancel);
|
||||
```
|
||||
|
||||
#### Expire Transaction
|
||||
You can Expire transaction with `transaction_status == PENDING` (before it become SETTLEMENT or EXPIRE)
|
||||
```php
|
||||
$cancel = \Midtrans\Transaction::cancel($orderId);
|
||||
var_dump($cancel);
|
||||
```
|
||||
|
||||
#### Refund Transaction
|
||||
Refund a transaction (not all payment channel allow refund via API)
|
||||
You can Refund transaction with `transaction_status == settlement`
|
||||
```php
|
||||
$params = array(
|
||||
'refund_key' => 'order1-ref1',
|
||||
'amount' => 10000,
|
||||
'reason' => 'Item out of stock'
|
||||
);
|
||||
$refund = \Midtrans\Transaction::refund($orderId, $params);
|
||||
var_dump($refund);
|
||||
```
|
||||
|
||||
#### Direct Refund Transaction
|
||||
Refund a transaction via Direct Refund API
|
||||
You can Refund transaction with `transaction_status == settlement`
|
||||
```php
|
||||
$params = array(
|
||||
'refund_key' => 'order1-ref1',
|
||||
'amount' => 10000,
|
||||
'reason' => 'Item out of stock'
|
||||
);
|
||||
$direct_refund = \Midtrans\Transaction::refundDirect($orderId, $params);
|
||||
var_dump($direct_refund);
|
||||
```
|
||||
|
||||
## Unit Test
|
||||
### Integration Test (sandbox real transactions)
|
||||
Please change server key and client key on `phpunit.xml` to your own.
|
||||
|
||||
### All Test
|
||||
`vendor/bin/phpunit`
|
||||
|
||||
### Specific Test
|
||||
`vendor/bin/phpunit tests/integration/CoreApiIntegrationTest.php`
|
||||
|
||||
## Contributing
|
||||
|
||||
### Developing e-commerce plug-ins
|
||||
|
||||
There are several guides that must be taken care of when you develop new plugins.
|
||||
|
||||
1. __Handling currency other than IDR.__ Midtrans `v1` and `v2` currently accepts payments in Indonesian Rupiah only. As a corrolary, there is a validation on the server to check whether the item prices are in integer or not. As much as you are tempted to round-off the price, DO NOT do that! Always prepare when your system uses currencies other than IDR, convert them to IDR accordingly, and only round the price AFTER that.
|
||||
|
||||
2. Consider using the __auto-sanitization__ feature.
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"name": "midtrans/midtrans-php",
|
||||
"description": "PHP Wrapper for Midtrans Payment API.",
|
||||
"homepage": "https://midtrans.com",
|
||||
"version": "2.5.2",
|
||||
"type": "library",
|
||||
"license":"MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Andri Setiawan",
|
||||
"email": "andri.setiawan@veritrans.co.id"
|
||||
},
|
||||
{
|
||||
"name": "Alvin Litani",
|
||||
"email": "alvin.litani@veritrans.co.id"
|
||||
},
|
||||
{
|
||||
"name": "Ismail Faruqi",
|
||||
"email": "ismail.faruqi@veritrans.co.id"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.4",
|
||||
"ext-curl": "*",
|
||||
"ext-json": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "5.7.*",
|
||||
"psy/psysh": "0.4.*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Midtrans\\": "Midtrans/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Midtrans\\":[
|
||||
"tests/",
|
||||
"test/integration",
|
||||
"tests/utility",
|
||||
"tests/utility/fixture"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
// This is just for very basic implementation reference, in production, you should validate the incoming requests and implement your backend more securely.
|
||||
// Please refer to this docs:
|
||||
// https://docs.midtrans.com/en/core-api/credit-card?id=_2-sending-transaction-data-to-charge-api
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
require_once dirname(__FILE__) . '/../../Midtrans.php';
|
||||
|
||||
Config::$serverKey = '<your server key>';
|
||||
// Uncomment for append and override notification URL
|
||||
// Config::$appendNotifUrl = "https://example.com";
|
||||
// Config::$overrideNotifUrl = "https://example.com";
|
||||
|
||||
// non-relevant function only used for demo/example purpose
|
||||
printExampleWarningMessage();
|
||||
|
||||
// Uncomment for production environment
|
||||
// Config::$isProduction = true;
|
||||
|
||||
// Uncomment to enable sanitization
|
||||
// Config::$isSanitized = true;
|
||||
|
||||
// Uncomment to enable idempotency-key, more details: (http://api-docs.midtrans.com/#idempotent-requests)
|
||||
// Config::$paymentIdempotencyKey = "Unique-ID";
|
||||
|
||||
$transaction_details = array(
|
||||
'order_id' => time(),
|
||||
'gross_amount' => 200000
|
||||
);
|
||||
|
||||
// Populate items
|
||||
$items = array(
|
||||
array(
|
||||
'id' => 'item1',
|
||||
'price' => 100000,
|
||||
'quantity' => 1,
|
||||
'name' => 'Adidas f50'
|
||||
),
|
||||
array(
|
||||
'id' => 'item2',
|
||||
'price' => 50000,
|
||||
'quantity' => 2,
|
||||
'name' => 'Nike N90'
|
||||
));
|
||||
|
||||
// Populate customer's billing address
|
||||
$billing_address = array(
|
||||
'first_name' => "Andri",
|
||||
'last_name' => "Setiawan",
|
||||
'address' => "Karet Belakang 15A, Setiabudi.",
|
||||
'city' => "Jakarta",
|
||||
'postal_code' => "51161",
|
||||
'phone' => "081322311801",
|
||||
'country_code' => 'IDN'
|
||||
);
|
||||
|
||||
// Populate customer's shipping address
|
||||
$shipping_address = array(
|
||||
'first_name' => "John",
|
||||
'last_name' => "Watson",
|
||||
'address' => "Bakerstreet 221B.",
|
||||
'city' => "Jakarta",
|
||||
'postal_code' => "51162",
|
||||
'phone' => "081322311801",
|
||||
'country_code' => 'IDN'
|
||||
);
|
||||
|
||||
// Populate customer's info
|
||||
$customer_details = array(
|
||||
'first_name' => "Andri",
|
||||
'last_name' => "Setiawan",
|
||||
'email' => "andri@setiawan.com",
|
||||
'phone' => "081322311801",
|
||||
'billing_address' => $billing_address,
|
||||
'shipping_address' => $shipping_address
|
||||
);
|
||||
|
||||
// Token ID from checkout page
|
||||
$token_id = $_POST['token_id'];
|
||||
$authentication = isset($_POST['secure']);
|
||||
$save_token_id = isset($_POST['save_cc']);
|
||||
|
||||
// Transaction data to be sent
|
||||
$transaction_data = array(
|
||||
'payment_type' => 'credit_card',
|
||||
'credit_card' => array(
|
||||
'token_id' => $token_id,
|
||||
'authentication' => $authentication,
|
||||
// 'bank' => 'bni', // optional acquiring bank
|
||||
'save_token_id' => $save_token_id
|
||||
),
|
||||
'transaction_details' => $transaction_details,
|
||||
'item_details' => $items,
|
||||
'customer_details' => $customer_details
|
||||
);
|
||||
|
||||
try {
|
||||
$response = CoreApi::charge($transaction_data);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($response);
|
||||
} catch (\Exception $e) {
|
||||
echo $e->getMessage();
|
||||
}
|
||||
|
||||
function printExampleWarningMessage() {
|
||||
if (strpos(Config::$serverKey, 'your ') != false ) {
|
||||
echo "<code>";
|
||||
echo "<h4>Please set your server key from sandbox</h4>";
|
||||
echo "In file: " . __FILE__;
|
||||
echo "<br>";
|
||||
echo "<br>";
|
||||
echo htmlspecialchars('Config::$serverKey = \'<your server key>\';');
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
<?php
|
||||
// This is just for very basic implementation reference, in production, you should validate the incoming requests and implement your backend more securely.
|
||||
// Please refer to this docs:
|
||||
// https://docs.midtrans.com/en/core-api/credit-card?id=_1-getting-the-card-token
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
require_once dirname(__FILE__) . '/../../Midtrans.php';
|
||||
// Set Your server key
|
||||
// can find in Merchant Portal -> Settings -> Access keys
|
||||
Config::$clientKey = '<your client key>';
|
||||
|
||||
// non-relevant function only used for demo/example purpose
|
||||
printExampleWarningMessage();
|
||||
|
||||
function printExampleWarningMessage() {
|
||||
if (strpos(Config::$clientKey, 'your ') != false ) {
|
||||
echo "<code>";
|
||||
echo "<h4>Please set your client key from sandbox</h4>";
|
||||
echo "In file: " . __FILE__;
|
||||
echo "<br>";
|
||||
echo "<br>";
|
||||
echo htmlspecialchars('Config::$clientKey = \'<your client key>\';');
|
||||
die();
|
||||
}
|
||||
}
|
||||
?>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Checkout</title>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/featherlight/1.7.12/featherlight.min.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script id="midtrans-script" type="text/javascript" src="https://api.midtrans.com/v2/assets/js/midtrans-new-3ds.min.js" data-environment="sandbox" data-client-key="<?php echo Config::$clientKey;?>"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/featherlight/1.7.12/featherlight.min.js"></script>
|
||||
|
||||
<h1>Checkout</h1>
|
||||
<form action="checkout-process.php" method="POST" id="payment-form">
|
||||
<fieldset>
|
||||
<legend>Checkout</legend>
|
||||
<small><strong>Field that may be presented to customer:</strong></small>
|
||||
<p>
|
||||
<label>Card Number</label>
|
||||
<input class="card-number" name="card-number" value="4811 1111 1111 1114" size="23" type="text" autocomplete="off" />
|
||||
</p>
|
||||
<p>
|
||||
<label>Expiration (MM/YYYY)</label>
|
||||
<input class="card-expiry-month" name="card-expiry-month" value="12" placeholder="MM" size="2" type="text" />
|
||||
<span> / </span>
|
||||
<input class="card-expiry-year" name="card-expiry-year" value="2025" placeholder="YYYY" size="4" type="text" />
|
||||
</p>
|
||||
<p>
|
||||
<label>CVV</label>
|
||||
<input class="card-cvv" name="card-cvv" value="123" size="4" type="password" autocomplete="off" />
|
||||
</p>
|
||||
<p>
|
||||
<label>Save credit card</label>
|
||||
<input type="checkbox" id="save_cc" name="save_cc" value="true">
|
||||
</p>
|
||||
<small><strong>Fields that shouldn't be presented to the customer:</strong></small>
|
||||
<p>
|
||||
<label>3D Secure</label>
|
||||
<input type="checkbox" id="secure" name="secure" value="true" checked>
|
||||
</p>
|
||||
<input id="token_id" name="token_id" type="hidden" />
|
||||
<button class="submit-button" type="submit">Submit Payment</button>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<code>
|
||||
<pre>
|
||||
<b>Testing cards:</b>
|
||||
|
||||
<b>For 3D Secure:</b>
|
||||
Visa 4811 1111 1111 1114
|
||||
MasterCard 5211 1111 1111 1117
|
||||
|
||||
<b>For Non 3D Secure:</b>
|
||||
Visa success 4011 1111 1111 1112
|
||||
Visa challenge 4111 1111 1111 1111
|
||||
Visa deny by FDS 4211 1111 1111 1110
|
||||
|
||||
MasterCard success 5481 1611 1111 1081
|
||||
MasterCard challenge 5110 1111 1111 1119
|
||||
MasterCard deny by FDS 5210 1111 1111 1118
|
||||
|
||||
</pre>
|
||||
</code>
|
||||
|
||||
<!-- Javascript for token generation -->
|
||||
<script type="text/javascript">
|
||||
$(function () {
|
||||
// open the console log to check the flow
|
||||
// 3ds new flow:
|
||||
// 1. get token_id
|
||||
// 2. send token_id to backend
|
||||
// 3. initial charge from backend to midtrans api
|
||||
// 4. open redirect_url
|
||||
|
||||
var options = {
|
||||
performAuthentication: function(redirect_url){
|
||||
openDialog(redirect_url);
|
||||
},
|
||||
onSuccess: function(response){
|
||||
console.log('success');
|
||||
console.log('response:',response);
|
||||
closeDialog();
|
||||
},
|
||||
onFailure: function(response){
|
||||
console.log('fail');
|
||||
console.log('response:',response);
|
||||
closeDialog();
|
||||
alert(response.status_message);
|
||||
$('button').removeAttr("disabled");
|
||||
},
|
||||
onPending: function(response){
|
||||
console.log('pending');
|
||||
console.log('response:',response);
|
||||
closeDialog();
|
||||
}
|
||||
};
|
||||
|
||||
function openDialog(url) {
|
||||
$.featherlight({
|
||||
iframe: url,
|
||||
iframeMaxWidth: '80%',
|
||||
iframeWidth: 700,
|
||||
iframeHeight: 500,
|
||||
closeOnClick: false,
|
||||
closeOnEsc: false,
|
||||
closeIcon:''
|
||||
});
|
||||
}
|
||||
|
||||
function closeDialog() {
|
||||
$.featherlight.close();
|
||||
}
|
||||
|
||||
$(".submit-button").click(function (event) {
|
||||
var card = {
|
||||
"card_number": $(".card-number").val(),
|
||||
"card_exp_month": $(".card-expiry-month").val(),
|
||||
"card_exp_year": $(".card-expiry-year").val(),
|
||||
"card_cvv": $(".card-cvv").val()
|
||||
};
|
||||
|
||||
event.preventDefault();
|
||||
$(this).attr("disabled", "disabled");
|
||||
|
||||
console.log('1. get token_id');
|
||||
MidtransNew3ds.getCardToken(card, getCardTokenCallback);
|
||||
return false;
|
||||
});
|
||||
|
||||
// callback functions
|
||||
var getCardTokenCallback = {
|
||||
onSuccess: function(response) {
|
||||
// Success to get card token_id, implement as you wish here
|
||||
console.log('Success to get card token_id, response:', response);
|
||||
var token_id = response.token_id;
|
||||
$("#token_id").val(token_id);
|
||||
|
||||
console.log('This is the card token_id:', token_id);
|
||||
// Implement sending the token_id to backend to proceed to next step
|
||||
console.log('2. send token_id to backend');
|
||||
// send token_id, save_cc and secure params
|
||||
// we send secure param for sample, in production, you should define transaction is secure/not in backend
|
||||
// we recommend always use secure=true
|
||||
// data: $("#token_id, #save_cc, #secure").serialize()
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: 'checkout-process.php',
|
||||
data: $("#token_id, #save_cc, #secure").serialize(),
|
||||
success: function(response){
|
||||
console.log('3. response charge from backend:', response);
|
||||
if (response.redirect_url){
|
||||
console.log('4. open redirect_url');
|
||||
MidtransNew3ds.authenticate(response.redirect_url, options);
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error){
|
||||
console.error(xhr);
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
onFailure: function(response) {
|
||||
// Fail to get card token_id, implement as you wish here
|
||||
console.log('Fail to get card token_id, response:', response);
|
||||
closeDialog();
|
||||
$('button').removeAttr("disabled");
|
||||
}
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
// This is just for very basic implementation reference, in production, you should validate the incoming requests and implement your backend more securely.
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
require_once dirname(__FILE__) . '/../../Midtrans.php';
|
||||
// Set Your server key
|
||||
// can find in Merchant Portal -> Settings -> Access keys
|
||||
Config::$serverKey = '<your server key>';
|
||||
|
||||
// non-relevant function only used for demo/example purpose
|
||||
printExampleWarningMessage();
|
||||
|
||||
// define variables and set to empty values
|
||||
$number = "";
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST") {
|
||||
$number = ($_POST["number"]);
|
||||
}
|
||||
|
||||
// required
|
||||
$params = array(
|
||||
"payment_type" => "gopay",
|
||||
"gopay_partner" => array(
|
||||
"phone_number" => $number,
|
||||
"redirect_url" => "https://www.google.com"
|
||||
)
|
||||
);
|
||||
|
||||
$response = '';
|
||||
try {
|
||||
$response = CoreApi::linkPaymentAccount($params);
|
||||
} catch (\Exception $e) {
|
||||
echo $e->getMessage();
|
||||
die();
|
||||
}
|
||||
|
||||
function printExampleWarningMessage() {
|
||||
if (strpos(Config::$serverKey, 'your ') != false ) {
|
||||
echo "<code>";
|
||||
echo "<h4>Please set your server key from sandbox</h4>";
|
||||
echo "In file: " . __FILE__;
|
||||
echo "<br>";
|
||||
echo "<br>";
|
||||
echo htmlspecialchars('Config::$serverKey = \'<your server key>\';');
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h2>Simple Gopay Tokenization</h2>
|
||||
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>">
|
||||
Phone number: <input type="text" name="number">
|
||||
<br><br>
|
||||
<input type="submit" name="submit" value="Submit">
|
||||
</form>
|
||||
|
||||
|
||||
<?php
|
||||
echo "<h2>Result get pay account:</h2>";
|
||||
echo json_encode($response, JSON_UNESCAPED_SLASHES);
|
||||
echo "<br>";
|
||||
?>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
// This is just for very basic implementation reference, in production, you should validate the incoming requests and implement your backend more securely.
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
require_once dirname(__FILE__) . '/../../Midtrans.php';
|
||||
// Set Your server key
|
||||
// can find in Merchant Portal -> Settings -> Access keys
|
||||
Config::$serverKey = '<your server key>';
|
||||
|
||||
// non-relevant function only used for demo/example purpose
|
||||
printExampleWarningMessage();
|
||||
|
||||
$orderId = '<your order id / transaction id>';
|
||||
// Get transaction status to Midtrans API
|
||||
$status = '';
|
||||
try {
|
||||
$status = Transaction::status($orderId);
|
||||
} catch (\Exception $e) {
|
||||
echo $e->getMessage();
|
||||
die();
|
||||
}
|
||||
|
||||
echo '<pre>';
|
||||
echo json_encode($status);
|
||||
|
||||
function printExampleWarningMessage() {
|
||||
if (strpos(Config::$serverKey, 'your ') != false ) {
|
||||
echo "<code>";
|
||||
echo "<h4>Please set your server key from sandbox</h4>";
|
||||
echo "In file: " . __FILE__;
|
||||
echo "<br>";
|
||||
echo "<br>";
|
||||
echo htmlspecialchars('Config::$serverKey = \'<your server key>\';');
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Approve a transaction that is in Challenge status
|
||||
// $approve = Transaction::approve($orderId);
|
||||
// var_dump($approve);
|
||||
|
||||
// Cancel a transaction
|
||||
// $cancel = Transaction::cancel($orderId);
|
||||
// var_dump($cancel);
|
||||
|
||||
// Expire a transaction
|
||||
// $expire = Transaction::expire($orderId);
|
||||
// var_dump($expire);
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
// This is just for very basic implementation reference, in production, you should validate the incoming requests and implement your backend more securely.
|
||||
// Please refer to this docs for sample HTTP notifications:
|
||||
// https://docs.midtrans.com/en/after-payment/http-notification?id=sample-of-different-payment-channels
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
require_once dirname(__FILE__) . '/../Midtrans.php';
|
||||
Config::$isProduction = false;
|
||||
Config::$serverKey = '<your server key>';
|
||||
|
||||
// non-relevant function only used for demo/example purpose
|
||||
printExampleWarningMessage();
|
||||
|
||||
try {
|
||||
$notif = new Notification();
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
exit($e->getMessage());
|
||||
}
|
||||
|
||||
$notif = $notif->getResponse();
|
||||
$transaction = $notif->transaction_status;
|
||||
$type = $notif->payment_type;
|
||||
$order_id = $notif->order_id;
|
||||
$fraud = $notif->fraud_status;
|
||||
|
||||
if ($transaction == 'capture') {
|
||||
// For credit card transaction, we need to check whether transaction is challenge by FDS or not
|
||||
if ($type == 'credit_card') {
|
||||
if ($fraud == 'challenge') {
|
||||
// TODO set payment status in merchant's database to 'Challenge by FDS'
|
||||
// TODO merchant should decide whether this transaction is authorized or not in MAP
|
||||
echo "Transaction order_id: " . $order_id ." is challenged by FDS";
|
||||
} else {
|
||||
// TODO set payment status in merchant's database to 'Success'
|
||||
echo "Transaction order_id: " . $order_id ." successfully captured using " . $type;
|
||||
}
|
||||
}
|
||||
} else if ($transaction == 'settlement') {
|
||||
// TODO set payment status in merchant's database to 'Settlement'
|
||||
echo "Transaction order_id: " . $order_id ." successfully transfered using " . $type;
|
||||
} else if ($transaction == 'pending') {
|
||||
// TODO set payment status in merchant's database to 'Pending'
|
||||
echo "Waiting customer to finish transaction order_id: " . $order_id . " using " . $type;
|
||||
} else if ($transaction == 'deny') {
|
||||
// TODO set payment status in merchant's database to 'Denied'
|
||||
echo "Payment using " . $type . " for transaction order_id: " . $order_id . " is denied.";
|
||||
} else if ($transaction == 'expire') {
|
||||
// TODO set payment status in merchant's database to 'expire'
|
||||
echo "Payment using " . $type . " for transaction order_id: " . $order_id . " is expired.";
|
||||
} else if ($transaction == 'cancel') {
|
||||
// TODO set payment status in merchant's database to 'Denied'
|
||||
echo "Payment using " . $type . " for transaction order_id: " . $order_id . " is canceled.";
|
||||
}
|
||||
|
||||
function printExampleWarningMessage() {
|
||||
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
|
||||
echo 'Notification-handler are not meant to be opened via browser / GET HTTP method. It is used to handle Midtrans HTTP POST notification / webhook.';
|
||||
}
|
||||
if (strpos(Config::$serverKey, 'your ') != false ) {
|
||||
echo "<code>";
|
||||
echo "<h4>Please set your server key from sandbox</h4>";
|
||||
echo "In file: " . __FILE__;
|
||||
echo "<br>";
|
||||
echo "<br>";
|
||||
echo htmlspecialchars('Config::$serverKey = \'<your server key>\';');
|
||||
die();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
// This is just for very basic implementation reference, in production, you should validate the incoming requests and implement your backend more securely.
|
||||
// Please refer to this docs for snap-redirect:
|
||||
// https://docs.midtrans.com/en/snap/integration-guide?id=alternative-way-to-display-snap-payment-page-via-redirect
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
require_once dirname(__FILE__) . '/../../Midtrans.php';
|
||||
// Set Your server key
|
||||
// can find in Merchant Portal -> Settings -> Access keys
|
||||
Config::$serverKey = '<your server key>';
|
||||
|
||||
// non-relevant function only used for demo/example purpose
|
||||
printExampleWarningMessage();
|
||||
|
||||
// Uncomment for production environment
|
||||
// Config::$isProduction = true;
|
||||
|
||||
// Uncomment to enable sanitization
|
||||
// Config::$isSanitized = true;
|
||||
|
||||
// Uncomment to enable 3D-Secure
|
||||
// Config::$is3ds = true;
|
||||
|
||||
// Required
|
||||
$transaction_details = array(
|
||||
'order_id' => rand(),
|
||||
'gross_amount' => 145000, // no decimal allowed for creditcard
|
||||
);
|
||||
|
||||
// Optional
|
||||
$item1_details = array(
|
||||
'id' => 'a1',
|
||||
'price' => 50000,
|
||||
'quantity' => 2,
|
||||
'name' => "Apple"
|
||||
);
|
||||
|
||||
// Optional
|
||||
$item2_details = array(
|
||||
'id' => 'a2',
|
||||
'price' => 45000,
|
||||
'quantity' => 1,
|
||||
'name' => "Orange"
|
||||
);
|
||||
|
||||
// Optional
|
||||
$item_details = array ($item1_details, $item2_details);
|
||||
|
||||
// Optional
|
||||
$billing_address = array(
|
||||
'first_name' => "Andri",
|
||||
'last_name' => "Litani",
|
||||
'address' => "Mangga 20",
|
||||
'city' => "Jakarta",
|
||||
'postal_code' => "16602",
|
||||
'phone' => "081122334455",
|
||||
'country_code' => 'IDN'
|
||||
);
|
||||
|
||||
// Optional
|
||||
$shipping_address = array(
|
||||
'first_name' => "Obet",
|
||||
'last_name' => "Supriadi",
|
||||
'address' => "Manggis 90",
|
||||
'city' => "Jakarta",
|
||||
'postal_code' => "16601",
|
||||
'phone' => "08113366345",
|
||||
'country_code' => 'IDN'
|
||||
);
|
||||
|
||||
// Optional
|
||||
$customer_details = array(
|
||||
'first_name' => "Andri",
|
||||
'last_name' => "Litani",
|
||||
'email' => "andri@litani.com",
|
||||
'phone' => "081122334455",
|
||||
'billing_address' => $billing_address,
|
||||
'shipping_address' => $shipping_address
|
||||
);
|
||||
|
||||
// Fill SNAP API parameter
|
||||
$params = array(
|
||||
'transaction_details' => $transaction_details,
|
||||
'customer_details' => $customer_details,
|
||||
'item_details' => $item_details,
|
||||
);
|
||||
|
||||
try {
|
||||
// Get Snap Payment Page URL
|
||||
$paymentUrl = Snap::createTransaction($params)->redirect_url;
|
||||
|
||||
// Redirect to Snap Payment Page
|
||||
header('Location: ' . $paymentUrl);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
echo $e->getMessage();
|
||||
}
|
||||
|
||||
function printExampleWarningMessage() {
|
||||
if (strpos(Config::$serverKey, 'your ') != false ) {
|
||||
echo "<code>";
|
||||
echo "<h4>Please set your server key from sandbox</h4>";
|
||||
echo "In file: " . __FILE__;
|
||||
echo "<br>";
|
||||
echo "<br>";
|
||||
echo htmlspecialchars('Config::$serverKey = \'<your server key>\';');
|
||||
die();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
$base = $_SERVER['REQUEST_URI'];
|
||||
?>
|
||||
|
||||
<form action="<?php echo $base ?>checkout-process.php" method="GET">
|
||||
<input type="submit" value="Pay with Snap Redirect">
|
||||
</form>
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
// This is just for very basic implementation reference, in production, you should validate the incoming requests and implement your backend more securely.
|
||||
// Please refer to this docs for snap popup:
|
||||
// https://docs.midtrans.com/en/snap/integration-guide?id=integration-steps-overview
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
require_once dirname(__FILE__) . '/../../Midtrans.php';
|
||||
// Set Your server key
|
||||
// can find in Merchant Portal -> Settings -> Access keys
|
||||
Config::$serverKey = 'SB-Mid-server-RxyojFyK90t-HrWwtDeV0QCO';
|
||||
Config::$clientKey = 'SB-Mid-client-NhMJk2e-7C27nGkV';
|
||||
|
||||
// non-relevant function only used for demo/example purpose
|
||||
printExampleWarningMessage();
|
||||
|
||||
// Uncomment for production environment
|
||||
// Config::$isProduction = true;
|
||||
Config::$isSanitized = Config::$is3ds = true;
|
||||
|
||||
// Required
|
||||
$transaction_details = array(
|
||||
'order_id' => rand(),
|
||||
'gross_amount' => 94000, // no decimal allowed for creditcard
|
||||
);
|
||||
// Optional
|
||||
$item_details = array (
|
||||
array(
|
||||
'id' => 'a1',
|
||||
'price' => 94000,
|
||||
'quantity' => 1,
|
||||
'name' => "Apple"
|
||||
),
|
||||
);
|
||||
// Optional
|
||||
$customer_details = array(
|
||||
'first_name' => "Andri",
|
||||
'last_name' => "Litani",
|
||||
'email' => "andri@litani.com",
|
||||
'phone' => "081122334455"
|
||||
);
|
||||
// Fill transaction details
|
||||
$transaction = array(
|
||||
'transaction_details' => $transaction_details,
|
||||
'customer_details' => $customer_details,
|
||||
'item_details' => $item_details,
|
||||
);
|
||||
|
||||
$snap_token = '';
|
||||
try {
|
||||
$snap_token = Snap::getSnapToken($transaction);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
echo $e->getMessage();
|
||||
}
|
||||
echo "snapToken = ".$snap_token;
|
||||
|
||||
function printExampleWarningMessage() {
|
||||
if (strpos(Config::$serverKey, 'your ') != false ) {
|
||||
echo "<code>";
|
||||
echo "<h4>Please set your server key from sandbox</h4>";
|
||||
echo "In file: " . __FILE__;
|
||||
echo "<br>";
|
||||
echo "<br>";
|
||||
echo htmlspecialchars('Config::$serverKey = \'<your server key>\';');
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>PAYMENT </title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
|
||||
</head>
|
||||
<body>
|
||||
<br>
|
||||
<br>
|
||||
<div class="container">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<p>Registrasi Berhasil, Selesaikan Pembayaran Sekarang</p>
|
||||
<button id="pay-button" class="btn btn-primary">PILIH METODE PEMBAYARAN</button>
|
||||
|
||||
<!-- TODO: Remove ".sandbox" from script src URL for production environment. Also input your client key in "data-client-key" -->
|
||||
<script src="https://app.sandbox.midtrans.com/snap/snap.js" data-client-key="<?php echo Config::$clientKey;?>"></script>
|
||||
<script type="text/javascript">
|
||||
document.getElementById('pay-button').onclick = function(){
|
||||
// SnapToken acquired from previous step
|
||||
snap.pay('<?php echo $snap_token?>');
|
||||
};
|
||||
</script>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
// This is just for very basic implementation reference, in production, you should validate the incoming requests and implement your backend more securely.
|
||||
// Please refer to this docs for snap popup:
|
||||
// https://docs.midtrans.com/en/snap/integration-guide?id=integration-steps-overview
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
require_once dirname(__FILE__) . '/../../Midtrans.php';
|
||||
// Set Your server key
|
||||
// can find in Merchant Portal -> Settings -> Access keys
|
||||
Config::$serverKey = '<your server key>';
|
||||
Config::$clientKey = '<your client key>';
|
||||
|
||||
// non-relevant function only used for demo/example purpose
|
||||
printExampleWarningMessage();
|
||||
|
||||
// Uncomment for production environment
|
||||
// Config::$isProduction = true;
|
||||
|
||||
// Enable sanitization
|
||||
Config::$isSanitized = true;
|
||||
|
||||
// Enable 3D-Secure
|
||||
Config::$is3ds = true;
|
||||
|
||||
// Uncomment for append and override notification URL
|
||||
// Config::$appendNotifUrl = "https://example.com";
|
||||
// Config::$overrideNotifUrl = "https://example.com";
|
||||
|
||||
// Required
|
||||
$transaction_details = array(
|
||||
'order_id' => rand(),
|
||||
'gross_amount' => 94000, // no decimal allowed for creditcard
|
||||
);
|
||||
|
||||
// Optional
|
||||
$item1_details = array(
|
||||
'id' => 'a1',
|
||||
'price' => 18000,
|
||||
'quantity' => 3,
|
||||
'name' => "Apple"
|
||||
);
|
||||
|
||||
// Optional
|
||||
$item2_details = array(
|
||||
'id' => 'a2',
|
||||
'price' => 20000,
|
||||
'quantity' => 2,
|
||||
'name' => "Orange"
|
||||
);
|
||||
|
||||
// Optional
|
||||
$item_details = array ($item1_details, $item2_details);
|
||||
|
||||
// Optional
|
||||
$billing_address = array(
|
||||
'first_name' => "Andri",
|
||||
'last_name' => "Litani",
|
||||
'address' => "Mangga 20",
|
||||
'city' => "Jakarta",
|
||||
'postal_code' => "16602",
|
||||
'phone' => "081122334455",
|
||||
'country_code' => 'IDN'
|
||||
);
|
||||
|
||||
// Optional
|
||||
$shipping_address = array(
|
||||
'first_name' => "Obet",
|
||||
'last_name' => "Supriadi",
|
||||
'address' => "Manggis 90",
|
||||
'city' => "Jakarta",
|
||||
'postal_code' => "16601",
|
||||
'phone' => "08113366345",
|
||||
'country_code' => 'IDN'
|
||||
);
|
||||
|
||||
// Optional
|
||||
$customer_details = array(
|
||||
'first_name' => "Andri",
|
||||
'last_name' => "Litani",
|
||||
'email' => "andri@litani.com",
|
||||
'phone' => "081122334455",
|
||||
'billing_address' => $billing_address,
|
||||
'shipping_address' => $shipping_address
|
||||
);
|
||||
|
||||
// Optional, remove this to display all available payment methods
|
||||
$enable_payments = array('credit_card','cimb_clicks','mandiri_clickpay','echannel');
|
||||
|
||||
// Fill transaction details
|
||||
$transaction = array(
|
||||
'enabled_payments' => $enable_payments,
|
||||
'transaction_details' => $transaction_details,
|
||||
'customer_details' => $customer_details,
|
||||
'item_details' => $item_details,
|
||||
);
|
||||
|
||||
$snap_token = '';
|
||||
try {
|
||||
$snap_token = Snap::getSnapToken($transaction);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
echo $e->getMessage();
|
||||
}
|
||||
|
||||
echo "snapToken = ".$snap_token;
|
||||
|
||||
function printExampleWarningMessage() {
|
||||
if (strpos(Config::$serverKey, 'your ') != false ) {
|
||||
echo "<code>";
|
||||
echo "<h4>Please set your server key from sandbox</h4>";
|
||||
echo "In file: " . __FILE__;
|
||||
echo "<br>";
|
||||
echo "<br>";
|
||||
echo htmlspecialchars('Config::$serverKey = \'<your server key>\';');
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<button id="pay-button">Pay!</button>
|
||||
<pre><div id="result-json">JSON result will appear here after payment:<br></div></pre>
|
||||
|
||||
<!-- TODO: Remove ".sandbox" from script src URL for production environment. Also input your client key in "data-client-key" -->
|
||||
<script src="https://app.sandbox.midtrans.com/snap/snap.js" data-client-key="<?php echo Config::$clientKey;?>"></script>
|
||||
<script type="text/javascript">
|
||||
document.getElementById('pay-button').onclick = function(){
|
||||
// SnapToken acquired from previous step
|
||||
snap.pay('<?php echo $snap_token?>', {
|
||||
// Optional
|
||||
onSuccess: function(result){
|
||||
/* You may add your own js here, this is just example */ document.getElementById('result-json').innerHTML += JSON.stringify(result, null, 2);
|
||||
},
|
||||
// Optional
|
||||
onPending: function(result){
|
||||
/* You may add your own js here, this is just example */ document.getElementById('result-json').innerHTML += JSON.stringify(result, null, 2);
|
||||
},
|
||||
// Optional
|
||||
onError: function(result){
|
||||
/* You may add your own js here, this is just example */ document.getElementById('result-json').innerHTML += JSON.stringify(result, null, 2);
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
$base = $_SERVER['REQUEST_URI'];
|
||||
?>
|
||||
|
||||
<h3>Selected Items:</h3>
|
||||
<ul>
|
||||
<li>Jeruk 2 kg x @20000</li>
|
||||
<li>Apel 3 kg x @18000</li>
|
||||
</ul>
|
||||
|
||||
<h4>Total: Rp 94.000,00</h4>
|
||||
|
||||
<form action="<?php echo $base ?>checkout-process.php" method="POST">
|
||||
<input type="hidden" name="amount" value="94000"/>
|
||||
<input type="submit" value="Confirm">
|
||||
</form>
|
||||
@@ -0,0 +1,12 @@
|
||||
> Warning: This note is for developer/maintainer of this package only
|
||||
|
||||
## Updating Package
|
||||
|
||||
- Make your changes
|
||||
- Update `version` value on `composer.json`
|
||||
- Update library version header on `ApiRequestor.php`
|
||||
- Commit and push changes to Github master branch
|
||||
- Create a [Github Release](https://github.com/Midtrans/midtrans-php/releases) with the target version
|
||||
- Github Release and Master Branch is automatically synced to [the Packagist version](https://packagist.org/packages/midtrans/midtrans-php)
|
||||
- Because of configured integration on Github & Packagist
|
||||
- To edit integration config sign in with the Midtrans Packagist Account
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit bootstrap="test_bootstrap.php"
|
||||
backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Unit Test">
|
||||
<directory>./tests/</directory>
|
||||
<exclude>./tests/integration</exclude>
|
||||
</testsuite>
|
||||
<testsuite name="All">
|
||||
<directory>./tests/</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Integration Test">
|
||||
<directory>./tests/integration</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<php>
|
||||
<env name="SERVER_KEY" value="SB-Mid-server-GwUP_WGbJPXsDzsNEBRs8IYA"/>
|
||||
<env name="CLIENT_KEY" value="SB-Mid-client-61XuGAwQ8Bj8LxSS"/>
|
||||
</php>
|
||||
</phpunit>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* Include test library if you are using composer
|
||||
* Example: Psysh (debugging library similar to pry in Ruby)
|
||||
*/
|
||||
require_once dirname(__FILE__) . '/vendor/autoload.php';
|
||||
|
||||
require_once dirname(__FILE__) . '/Midtrans.php';
|
||||
require_once dirname(__FILE__) . '/tests/Mt_Tests.php';
|
||||
require_once dirname(__FILE__) . '/tests/utility/MtFixture.php';
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
class MT_Tests
|
||||
{
|
||||
public static $stubHttp = false;
|
||||
public static $stubHttpResponse;
|
||||
public static $stubHttpStatus;
|
||||
public static $lastHttpRequest;
|
||||
|
||||
public static function reset()
|
||||
{
|
||||
MT_Tests::$stubHttp = false;
|
||||
MT_Tests::$stubHttpResponse = null;
|
||||
MT_Tests::$lastHttpRequest = null;
|
||||
}
|
||||
|
||||
public static function lastReqOptions()
|
||||
{
|
||||
$consts = array(
|
||||
CURLOPT_URL => "URL",
|
||||
CURLOPT_HTTPHEADER => "HTTPHEADER",
|
||||
CURLOPT_RETURNTRANSFER => "RETURNTRANSFER",
|
||||
CURLOPT_CAINFO => "CAINFO",
|
||||
CURLOPT_POST => "POST",
|
||||
CURLOPT_POSTFIELDS => "POSTFIELDS",
|
||||
CURLOPT_PROXY => "PROXY"
|
||||
);
|
||||
|
||||
$options = array();
|
||||
foreach (MT_Tests::$lastHttpRequest["curl"] as $intValue => $value) {
|
||||
$key = $consts[$intValue] ? $consts[$intValue] : $intValue;
|
||||
$options[$key] = $value;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
class MidtransApiRequestorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
public function testConfigOptionsOverrideCurlOptions()
|
||||
{
|
||||
MT_Tests::$stubHttp = true;
|
||||
MT_Tests::$stubHttpResponse = '{ "status_code": "200" }';
|
||||
|
||||
Config::$curlOptions = array(
|
||||
CURLOPT_HTTPHEADER => array( "User-Agent: testing lib" ),
|
||||
CURLOPT_PROXY => "http://proxy.com"
|
||||
);
|
||||
|
||||
$resp = ApiRequestor::post("http://proxy.com", "dummy", "");
|
||||
|
||||
$fields = MT_Tests::lastReqOptions();
|
||||
$this->assertTrue(in_array("User-Agent: testing lib", $fields["HTTPHEADER"]));
|
||||
$this->assertTrue(in_array('Content-Type: application/json', $fields["HTTPHEADER"]));
|
||||
|
||||
$this->assertEquals("http://proxy.com", $fields["PROXY"]);
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
MT_Tests::reset();
|
||||
Config::$curlOptions = array();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
class MidtransConfigTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
public function testReturnBaseUrl()
|
||||
{
|
||||
Config::$isProduction = false;
|
||||
$this->assertEquals(
|
||||
Config::getBaseUrl(),
|
||||
Config::SANDBOX_BASE_URL
|
||||
);
|
||||
|
||||
Config::$isProduction = true;
|
||||
$this->assertEquals(Config::PRODUCTION_BASE_URL, Config::getBaseUrl());
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
Config::$isProduction = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
class MidtransCoreApiTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
public function testCharge()
|
||||
{
|
||||
Config::$appendNotifUrl = "https://example.com";
|
||||
Config::$overrideNotifUrl = "https://example.com";
|
||||
Config::$paymentIdempotencyKey = "123456";
|
||||
Config::$serverKey = "dummy";
|
||||
MT_Tests::$stubHttp = true;
|
||||
MT_Tests::$stubHttpResponse = '{
|
||||
"status_code": 200,
|
||||
"redirect_url": "http://host.com/pay"
|
||||
}';
|
||||
|
||||
$params = array(
|
||||
'transaction_details' => array(
|
||||
'order_id' => "Order-111",
|
||||
'gross_amount' => 10000,
|
||||
)
|
||||
);
|
||||
|
||||
$charge = CoreApi::charge($params);
|
||||
|
||||
$this->assertEquals($charge->status_code, "200");
|
||||
|
||||
$this->assertEquals(
|
||||
MT_Tests::$lastHttpRequest["url"],
|
||||
"https://api.sandbox.midtrans.com/v2/charge"
|
||||
);
|
||||
|
||||
$fields = MT_Tests::lastReqOptions();
|
||||
$this->assertEquals($fields["POST"], 1);
|
||||
$this->assertEquals(
|
||||
$fields["POSTFIELDS"],
|
||||
'{"payment_type":"credit_card","transaction_details":{"order_id":"Order-111","gross_amount":10000}}'
|
||||
);
|
||||
$this->assertTrue(in_array('X-Append-Notification: https://example.com', $fields["HTTPHEADER"]));
|
||||
$this->assertTrue(in_array('X-Override-Notification: https://example.com', $fields["HTTPHEADER"]));
|
||||
$this->assertTrue(in_array('Idempotency-Key: 123456', $fields["HTTPHEADER"]));
|
||||
}
|
||||
|
||||
public function testRealConnectWithInvalidKey()
|
||||
{
|
||||
Config::$serverKey = 'invalid-server-key';
|
||||
$params = array(
|
||||
'transaction_details' => array(
|
||||
'order_id' => rand(),
|
||||
'gross_amount' => 10000,
|
||||
)
|
||||
);
|
||||
|
||||
try {
|
||||
$paymentUrl = CoreApi::charge($params);
|
||||
} catch (\Exception $error) {
|
||||
$this->assertContains("Midtrans API is returning API error. HTTP status code: 401", $error->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function testCapture()
|
||||
{
|
||||
MT_Tests::$stubHttp = true;
|
||||
MT_Tests::$stubHttpResponse = '{
|
||||
"status_code": "200",
|
||||
"status_message": "Success, Credit Card capture transaction is successful",
|
||||
"transaction_id": "1ac1a089d-a587-40f1-a936-a7770667d6dd",
|
||||
"order_id": "A27550",
|
||||
"payment_type": "credit_card",
|
||||
"transaction_time": "2014-08-25 10:20:54",
|
||||
"transaction_status": "capture",
|
||||
"fraud_status": "accept",
|
||||
"masked_card": "481111-1114",
|
||||
"bank": "bni",
|
||||
"approval_code": "1408937217061",
|
||||
"gross_amount": "55000.00"
|
||||
}';
|
||||
|
||||
$capture = CoreApi::capture("A27550");
|
||||
|
||||
$this->assertEquals($capture->status_code, "200");
|
||||
|
||||
$this->assertEquals("https://api.sandbox.midtrans.com/v2/capture", MT_Tests::$lastHttpRequest["url"]);
|
||||
|
||||
$fields = MT_Tests::lastReqOptions();
|
||||
$this->assertEquals(1, $fields["POST"]);
|
||||
$this->assertEquals('{"transaction_id":"A27550"}', $fields["POSTFIELDS"]);
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
MT_Tests::reset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
require_once dirname(__FILE__) . '/../Midtrans.php';
|
||||
|
||||
define(
|
||||
'TEST_CAPTURE_JSON', '{
|
||||
"status_code" : "200",
|
||||
"status_message" : "Midtrans payment notification",
|
||||
"transaction_id" : "826acc53-14e0-4ae7-95e2-845bf0311579",
|
||||
"order_id" : "2014040745",
|
||||
"payment_type" : "credit_card",
|
||||
"transaction_time" : "2014-04-07 16:22:36",
|
||||
"transaction_status" : "capture",
|
||||
"fraud_status" : "accept",
|
||||
"masked_card" : "411111-1111",
|
||||
"gross_amount" : "2700"
|
||||
}'
|
||||
);
|
||||
|
||||
class MidtransNotificationTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
public function testCanWorkWithJSON()
|
||||
{
|
||||
$tmpfname = tempnam(sys_get_temp_dir(), "midtrans_test");
|
||||
file_put_contents($tmpfname, TEST_CAPTURE_JSON);
|
||||
|
||||
MT_Tests::$stubHttp = true;
|
||||
MT_Tests::$stubHttpResponse = TEST_CAPTURE_JSON;
|
||||
|
||||
Config::$serverKey = 'dummy';
|
||||
$notif = new Notification($tmpfname);
|
||||
|
||||
$this->assertEquals("capture", $notif->transaction_status);
|
||||
$this->assertEquals("credit_card", $notif->payment_type);
|
||||
$this->assertEquals("2014040745", $notif->order_id);
|
||||
$this->assertEquals("2700", $notif->gross_amount);
|
||||
|
||||
unlink($tmpfname);
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
MT_Tests::reset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
use Midtrans\utility\MtChargeFixture;
|
||||
|
||||
class MidtransSanitizerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
public function testSanitizeWithoutOptionalRequest()
|
||||
{
|
||||
$params = MtChargeFixture::build('vtweb');
|
||||
unset($params['customer_details']);
|
||||
|
||||
Sanitizer::jsonRequest($params);
|
||||
$this->assertEquals(false, isset($params['customer_details']));
|
||||
}
|
||||
|
||||
public function testSanitizeWithoutOptionalCustDetails()
|
||||
{
|
||||
$params = MtChargeFixture::build('vtweb');
|
||||
unset($params['customer_details']['first_name']);
|
||||
unset($params['customer_details']['last_name']);
|
||||
unset($params['customer_details']['email']);
|
||||
unset($params['customer_details']['billing_address']);
|
||||
unset($params['customer_details']['shipping_address']);
|
||||
|
||||
Sanitizer::jsonRequest($params);
|
||||
|
||||
$this->assertEquals(false, isset($params['customer_details']['first_name']));
|
||||
$this->assertEquals(false, isset($params['customer_details']['last_name']));
|
||||
$this->assertEquals(false, isset($params['customer_details']['email']));
|
||||
$this->assertEquals(false, isset($params['customer_details']['billing_address']));
|
||||
$this->assertEquals(false, isset($params['customer_details']['shipping_address']));
|
||||
}
|
||||
|
||||
public function testSanitizeWithoutOptionalInBillingAddress()
|
||||
{
|
||||
$params = MtChargeFixture::build('vtweb');
|
||||
unset($params['customer_details']['billing_address']['first_name']);
|
||||
unset($params['customer_details']['billing_address']['last_name']);
|
||||
unset($params['customer_details']['billing_address']['phone']);
|
||||
unset($params['customer_details']['billing_address']['address']);
|
||||
unset($params['customer_details']['billing_address']['city']);
|
||||
unset($params['customer_details']['billing_address']['postal_code']);
|
||||
unset($params['customer_details']['billing_address']['country_code']);
|
||||
|
||||
Sanitizer::jsonRequest($params);
|
||||
|
||||
$this->assertEquals(false, isset($params['customer_details']['billing_address']['first_name']));
|
||||
$this->assertEquals(false, isset($params['customer_details']['billing_address']['last_name']));
|
||||
$this->assertEquals(false, isset($params['customer_details']['billing_address']['phone']));
|
||||
$this->assertEquals(false, isset($params['customer_details']['billing_address']['address']));
|
||||
$this->assertEquals(false, isset($params['customer_details']['billing_address']['city']));
|
||||
$this->assertEquals(false, isset($params['customer_details']['billing_address']['postal_code']));
|
||||
$this->assertEquals(false, isset($params['customer_details']['billing_address']['country_code']));
|
||||
}
|
||||
|
||||
public function testSanitizeWithoutOptionalInShippingAddress()
|
||||
{
|
||||
$params = MtChargeFixture::build('vtweb');
|
||||
unset($params['customer_details']['shipping_address']['first_name']);
|
||||
unset($params['customer_details']['shipping_address']['last_name']);
|
||||
unset($params['customer_details']['shipping_address']['phone']);
|
||||
unset($params['customer_details']['shipping_address']['address']);
|
||||
unset($params['customer_details']['shipping_address']['city']);
|
||||
unset($params['customer_details']['shipping_address']['postal_code']);
|
||||
unset($params['customer_details']['shipping_address']['country_code']);
|
||||
|
||||
Sanitizer::jsonRequest($params);
|
||||
|
||||
$this->assertEquals(false, isset($params['customer_details']['shipping_address']['first_name']));
|
||||
$this->assertEquals(false, isset($params['customer_details']['shipping_address']['last_name']));
|
||||
$this->assertEquals(false, isset($params['customer_details']['shipping_address']['phone']));
|
||||
$this->assertEquals(false, isset($params['customer_details']['shipping_address']['address']));
|
||||
$this->assertEquals(false, isset($params['customer_details']['shipping_address']['city']));
|
||||
$this->assertEquals(false, isset($params['customer_details']['shipping_address']['postal_code']));
|
||||
$this->assertEquals(false, isset($params['customer_details']['shipping_address']['country_code']));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
class MidtransSnapApiRequestorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
public function testConfigOptionsOverrideCurlOptions()
|
||||
{
|
||||
MT_Tests::$stubHttp = true;
|
||||
MT_Tests::$stubHttpResponse = '{ "status_code": "200" }';
|
||||
MT_Tests::$stubHttpStatus = array('http_code' => 201);
|
||||
|
||||
Config::$curlOptions = array(
|
||||
CURLOPT_HTTPHEADER => array( "User-Agent: testing lib" ),
|
||||
CURLOPT_PROXY => "http://proxy.com"
|
||||
);
|
||||
|
||||
$resp = ApiRequestor::post("http://example.com", "dummy", "");
|
||||
|
||||
$fields = MT_Tests::lastReqOptions();
|
||||
$this->assertTrue(in_array("User-Agent: testing lib", $fields["HTTPHEADER"]));
|
||||
$this->assertTrue(in_array('Content-Type: application/json', $fields["HTTPHEADER"]));
|
||||
|
||||
$this->assertEquals("http://proxy.com", $fields["PROXY"]);
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
MT_Tests::reset();
|
||||
Config::$curlOptions = array();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace Midtrans;
|
||||
|
||||
class MidtransSnapTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
public function testGetSnapToken()
|
||||
{
|
||||
Config::$serverKey = 'MyVerySecretKey';
|
||||
Config::$appendNotifUrl = "https://example.com";
|
||||
Config::$overrideNotifUrl = "https://example.com";
|
||||
MT_Tests::$stubHttp = true;
|
||||
MT_Tests::$stubHttpResponse = '{ "token": "abcdefghijklmnopqrstuvwxyz" }';
|
||||
MT_Tests::$stubHttpStatus = array('http_code' => 201);
|
||||
|
||||
$params = array(
|
||||
'transaction_details' => array(
|
||||
'order_id' => "Order-111",
|
||||
'gross_amount' => 10000,
|
||||
)
|
||||
);
|
||||
|
||||
$tokenId = Snap::getSnapToken($params);
|
||||
|
||||
$this->assertEquals("abcdefghijklmnopqrstuvwxyz", $tokenId);
|
||||
|
||||
$this->assertEquals(
|
||||
"https://app.sandbox.midtrans.com/snap/v1/transactions",
|
||||
MT_Tests::$lastHttpRequest["url"]
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
'MyVerySecretKey',
|
||||
MT_Tests::$lastHttpRequest["server_key"]
|
||||
);
|
||||
|
||||
$fields = MT_Tests::lastReqOptions();
|
||||
|
||||
$this->assertEquals(1, $fields["POST"]);
|
||||
$this->assertTrue(in_array('X-Append-Notification: https://example.com', $fields["HTTPHEADER"]));
|
||||
$this->assertTrue(in_array('X-Override-Notification: https://example.com', $fields["HTTPHEADER"]));
|
||||
$this->assertEquals(
|
||||
$fields["POSTFIELDS"],
|
||||
'{"credit_card":{"secure":false},' .
|
||||
'"transaction_details":{"order_id":"Order-111","gross_amount":10000}}'
|
||||
);
|
||||
}
|
||||
|
||||
public function testGrossAmount()
|
||||
{
|
||||
$params = array(
|
||||
'transaction_details' => array(
|
||||
'order_id' => rand()
|
||||
),
|
||||
'item_details' => array( array( 'price' => 10000, 'quantity' => 5 ) )
|
||||
);
|
||||
|
||||
MT_Tests::$stubHttp = true;
|
||||
MT_Tests::$stubHttpResponse = '{ "token": "abcdefghijklmnopqrstuvwxyz" }';
|
||||
MT_Tests::$stubHttpStatus = array('http_code' => 201);
|
||||
|
||||
$tokenId = Snap::getSnapToken($params);
|
||||
|
||||
$this->assertEquals(
|
||||
50000,
|
||||
MT_Tests::$lastHttpRequest['data_hash']['transaction_details']['gross_amount']
|
||||
);
|
||||
}
|
||||
|
||||
public function testOverrideParams()
|
||||
{
|
||||
$params = array(
|
||||
'echannel' => array(
|
||||
'bill_info1' => 'bill_value1'
|
||||
)
|
||||
);
|
||||
|
||||
MT_Tests::$stubHttp = true;
|
||||
MT_Tests::$stubHttpResponse = '{ "token": "abcdefghijklmnopqrstuvwxyz" }';
|
||||
MT_Tests::$stubHttpStatus = array('http_code' => 201);
|
||||
|
||||
$tokenId = Snap::getSnapToken($params);
|
||||
|
||||
$this->assertEquals(
|
||||
array('bill_info1' => 'bill_value1'),
|
||||
MT_Tests::$lastHttpRequest['data_hash']['echannel']
|
||||
);
|
||||
}
|
||||
|
||||
public function testRealConnect()
|
||||
{
|
||||
$params = array(
|
||||
'transaction_details' => array(
|
||||
'order_id' => rand(),
|
||||
'gross_amount' => 10000,
|
||||
)
|
||||
);
|
||||
|
||||
try {
|
||||
$tokenId = Snap::getSnapToken($params);
|
||||
} catch (\Exception $error) {
|
||||
$errorHappen = true;
|
||||
$this->assertContains(
|
||||
"authorized",
|
||||
$error->getMessage()
|
||||
);
|
||||
}
|
||||
|
||||
$this->assertTrue($errorHappen);
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
MT_Tests::reset();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
<?php
|
||||
namespace Midtrans;
|
||||
|
||||
class MidtransTransactionTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
||||
public function testStatus()
|
||||
{
|
||||
Config::$serverKey = 'MyVerySecretKey';
|
||||
MT_Tests::$stubHttp = true;
|
||||
MT_Tests::$stubHttpResponse = '{
|
||||
"status_code": "200",
|
||||
"status_message": "Success, transaction found",
|
||||
"transaction_id": "e3b8c383-55b4-4223-bd77-15c48c0245ca",
|
||||
"masked_card": "481111-1114",
|
||||
"order_id": "Order-111",
|
||||
"payment_type": "credit_card",
|
||||
"transaction_time": "2014-11-21 13:07:50",
|
||||
"transaction_status": "settlement",
|
||||
"fraud_status": "accept",
|
||||
"approval_code": "1416550071152",
|
||||
"signature_key": "4ef8218aad5b64bae2ec9d6b0f0a0b059b88bd...",
|
||||
"bank": "mandiri",
|
||||
"gross_amount": "10000.00"
|
||||
}';
|
||||
|
||||
$status = Transaction::status("Order-111");
|
||||
|
||||
$this->assertEquals("200", $status->status_code);
|
||||
$this->assertEquals("Order-111", $status->order_id);
|
||||
$this->assertEquals("1416550071152", $status->approval_code);
|
||||
|
||||
$this->assertEquals(
|
||||
"https://api.sandbox.midtrans.com/v2/Order-111/status",
|
||||
MT_Tests::$lastHttpRequest["url"]
|
||||
);
|
||||
|
||||
$fields = MT_Tests::lastReqOptions();
|
||||
$this->assertFalse(isset($fields['POST']));
|
||||
$this->assertFalse(isset($fields['POSTFIELDS']));
|
||||
}
|
||||
|
||||
public function testFailureStatus()
|
||||
{
|
||||
Config::$serverKey = 'MyVerySecretKey';
|
||||
MT_Tests::$stubHttp = true;
|
||||
MT_Tests::$stubHttpResponse = '{
|
||||
"status_code": "404",
|
||||
"status_message": "The requested resource is not found"
|
||||
}';
|
||||
|
||||
try {
|
||||
$status = Transaction::status("Order-111");
|
||||
} catch (\Exception $error) {
|
||||
$errorHappen = true;
|
||||
$this->assertEquals(404, $error->getCode());
|
||||
}
|
||||
|
||||
$this->assertTrue($errorHappen);
|
||||
MT_Tests::reset();
|
||||
}
|
||||
|
||||
public function testRealStatus()
|
||||
{
|
||||
Config::$serverKey = 'MyVerySecretKey';
|
||||
try {
|
||||
$status = Transaction::status("Order-111");
|
||||
} catch (\Exception $error) {
|
||||
$errorHappen = true;
|
||||
$this->assertContains("Midtrans API is returning API error. HTTP status code: 401", $error->getMessage());
|
||||
}
|
||||
|
||||
$this->assertTrue($errorHappen);
|
||||
}
|
||||
|
||||
public function testApprove()
|
||||
{
|
||||
Config::$serverKey = 'MyVerySecretKey';
|
||||
MT_Tests::$stubHttp = true;
|
||||
MT_Tests::$stubHttpResponse = '{
|
||||
"status_code": "200",
|
||||
"status_message": "Success, transaction is approved",
|
||||
"transaction_id": "2af158d4-b82e-46ac-808b-be19aaa96ce3",
|
||||
"masked_card": "451111-1117",
|
||||
"order_id": "Order-111",
|
||||
"payment_type": "credit_card",
|
||||
"transaction_time": "2014-11-27 10:05:10",
|
||||
"transaction_status": "capture",
|
||||
"fraud_status": "accept",
|
||||
"approval_code": "1416550071152",
|
||||
"bank": "bni",
|
||||
"gross_amount": "10000.00"
|
||||
}';
|
||||
|
||||
$approve = Transaction::approve("Order-111");
|
||||
|
||||
$this->assertEquals("200", $approve);
|
||||
|
||||
$this->assertEquals(
|
||||
"https://api.sandbox.midtrans.com/v2/Order-111/approve",
|
||||
MT_Tests::$lastHttpRequest["url"]
|
||||
);
|
||||
|
||||
$fields = MT_Tests::lastReqOptions();
|
||||
$this->assertEquals(1, $fields["POST"]);
|
||||
$this->assertEquals(null, $fields["POSTFIELDS"]);
|
||||
}
|
||||
|
||||
public function testCancel()
|
||||
{
|
||||
Config::$serverKey = 'MyVerySecretKey';
|
||||
MT_Tests::$stubHttp = true;
|
||||
MT_Tests::$stubHttpResponse = '{
|
||||
"status_code": "200",
|
||||
"status_message": "Success, transaction is canceled",
|
||||
"transaction_id": "2af158d4-b82e-46ac-808b-be19aaa96ce3",
|
||||
"masked_card": "451111-1117",
|
||||
"order_id": "Order-111",
|
||||
"payment_type": "credit_card",
|
||||
"transaction_time": "2014-11-27 10:05:10",
|
||||
"transaction_status": "cancel",
|
||||
"fraud_status": "accept",
|
||||
"approval_code": "1416550071152",
|
||||
"bank": "bni",
|
||||
"gross_amount": "10000.00"
|
||||
}';
|
||||
|
||||
$cancel = Transaction::cancel("Order-111");
|
||||
|
||||
$this->assertEquals("200", $cancel);
|
||||
|
||||
$this->assertEquals(
|
||||
"https://api.sandbox.midtrans.com/v2/Order-111/cancel",
|
||||
MT_Tests::$lastHttpRequest["url"]
|
||||
);
|
||||
|
||||
$fields = MT_Tests::lastReqOptions();
|
||||
$this->assertEquals(1, $fields["POST"]);
|
||||
$this->assertEquals(null, $fields["POSTFIELDS"]);
|
||||
}
|
||||
|
||||
public function testExpire()
|
||||
{
|
||||
Config::$serverKey = 'MyVerySecretKey';
|
||||
MT_Tests::$stubHttp = true;
|
||||
MT_Tests::$stubHttpResponse = '{
|
||||
"status_code": "407",
|
||||
"status_message": "Success, transaction has expired",
|
||||
"transaction_id": "2af158d4-b82e-46ac-808b-be19aaa96ce3",
|
||||
"order_id": "Order-111",
|
||||
"payment_type": "echannel",
|
||||
"transaction_time": "2014-11-27 10:05:10",
|
||||
"transaction_status": "expire",
|
||||
"gross_amount": "10000.00"
|
||||
}';
|
||||
|
||||
$expire = Transaction::expire("Order-111");
|
||||
|
||||
$this->assertEquals("407", $expire->status_code);
|
||||
$this->assertEquals("Success, transaction has expired", $expire->status_message);
|
||||
|
||||
$this->assertEquals(
|
||||
"https://api.sandbox.midtrans.com/v2/Order-111/expire",
|
||||
MT_Tests::$lastHttpRequest["url"]
|
||||
);
|
||||
|
||||
$fields = MT_Tests::lastReqOptions();
|
||||
$this->assertEquals(1, $fields["POST"]);
|
||||
$this->assertEquals(null, $fields["POSTFIELDS"]);
|
||||
}
|
||||
|
||||
public function testRefund()
|
||||
{
|
||||
Config::$serverKey = 'MyVerySecretKey';
|
||||
MT_Tests::$stubHttp = true;
|
||||
MT_Tests::$stubHttpResponse = '{
|
||||
"status_code": "200",
|
||||
"status_message": "Success, refund request is approved",
|
||||
"transaction_id": "447e846a-403e-47db-a5da-d7f3f06375d6",
|
||||
"order_id": "Order-111",
|
||||
"payment_type": "credit_card",
|
||||
"transaction_time": "2015-06-15 13:36:24",
|
||||
"transaction_status": "refund",
|
||||
"gross_amount": "10000.00",
|
||||
"refund_chargeback_id": 1,
|
||||
"refund_amount": "10000.00",
|
||||
"refund_key": "reference1"
|
||||
}';
|
||||
|
||||
$params = array(
|
||||
'refund_key' => 'reference1',
|
||||
'amount' => 10000,
|
||||
'reason' => 'Item out of stock'
|
||||
);
|
||||
$refund = Transaction::refund("Order-111",$params);
|
||||
|
||||
$this->assertEquals("200", $refund->status_code);
|
||||
|
||||
$this->assertEquals(
|
||||
"https://api.sandbox.midtrans.com/v2/Order-111/refund",
|
||||
MT_Tests::$lastHttpRequest["url"]
|
||||
);
|
||||
|
||||
$fields = MT_Tests::lastReqOptions();
|
||||
$this->assertEquals(1, $fields["POST"]);
|
||||
$this->assertEquals(
|
||||
'{"refund_key":"reference1","amount":10000,"reason":"Item out of stock"}',
|
||||
$fields["POSTFIELDS"]);
|
||||
}
|
||||
|
||||
public function testRefundDirect()
|
||||
{
|
||||
Config::$serverKey = 'MyVerySecretKey';
|
||||
MT_Tests::$stubHttp = true;
|
||||
MT_Tests::$stubHttpResponse = '{
|
||||
"status_code": "200",
|
||||
"status_message": "Success, refund request is approved",
|
||||
"transaction_id": "447e846a-403e-47db-a5da-d7f3f06375d6",
|
||||
"order_id": "Order-111",
|
||||
"payment_type": "credit_card",
|
||||
"transaction_time": "2015-06-15 13:36:24",
|
||||
"transaction_status": "refund",
|
||||
"gross_amount": "10000.00",
|
||||
"refund_chargeback_id": 1,
|
||||
"refund_amount": "10000.00",
|
||||
"refund_key": "reference1"
|
||||
}';
|
||||
|
||||
$params = array(
|
||||
'refund_key' => 'reference1',
|
||||
'amount' => 10000,
|
||||
'reason' => 'Item out of stock'
|
||||
);
|
||||
$refund = Transaction::refundDirect("Order-111", $params);
|
||||
$this->assertEquals("200", $refund->status_code);
|
||||
|
||||
$this->assertEquals(
|
||||
"https://api.sandbox.midtrans.com/v2/Order-111/refund/online/direct",
|
||||
MT_Tests::$lastHttpRequest["url"]
|
||||
);
|
||||
$fields = MT_Tests::lastReqOptions();
|
||||
$this->assertEquals(1, $fields["POST"]);
|
||||
$this->assertEquals(
|
||||
'{"refund_key":"reference1","amount":10000,"reason":"Item out of stock"}',
|
||||
$fields["POSTFIELDS"]);
|
||||
}
|
||||
|
||||
public function testDeny()
|
||||
{
|
||||
Config::$serverKey = 'MyVerySecretKey';
|
||||
MT_Tests::$stubHttp = true;
|
||||
MT_Tests::$stubHttpResponse = '{
|
||||
"status_code" : "200",
|
||||
"status_message" : "Success, transaction is denied",
|
||||
"transaction_id" : "ca297170-be4c-45ed-9dc9-be5ba99d30ee",
|
||||
"masked_card" : "451111-1117",
|
||||
"order_id" : "Order-111",
|
||||
"payment_type" : "credit_card",
|
||||
"transaction_time" : "2014-10-31 14:46:44",
|
||||
"transaction_status" : "deny",
|
||||
"fraud_status" : "deny",
|
||||
"bank" : "bni",
|
||||
"gross_amount" : "30000.00"
|
||||
}';
|
||||
|
||||
$deny = Transaction::deny("Order-111");
|
||||
|
||||
$this->assertEquals("200", $deny->status_code);
|
||||
|
||||
$this->assertEquals(
|
||||
"https://api.sandbox.midtrans.com/v2/Order-111/deny",
|
||||
MT_Tests::$lastHttpRequest["url"]
|
||||
);
|
||||
|
||||
$fields = MT_Tests::lastReqOptions();
|
||||
$this->assertEquals(1, $fields["POST"]);
|
||||
$this->assertEquals(null, $fields["POSTFIELDS"]);
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
MT_Tests::reset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
<?php
|
||||
|
||||
namespace Midtrans\integration;
|
||||
|
||||
use Midtrans\CoreApi;
|
||||
use Midtrans\utility\MtChargeFixture;
|
||||
|
||||
require_once 'IntegrationTest.php';
|
||||
|
||||
class CoreApiIntegrationTest extends IntegrationTest
|
||||
{
|
||||
private $payment_type;
|
||||
private $charge_params;
|
||||
private $charge_response;
|
||||
|
||||
public function prepareChargeParams($payment_type, $payment_data = null)
|
||||
{
|
||||
$this->payment_type = $payment_type;
|
||||
$this->charge_params = MtChargeFixture::build($payment_type, $payment_data);
|
||||
}
|
||||
|
||||
public function testCardRegister()
|
||||
{
|
||||
$this->charge_response = CoreApi::cardRegister("4811111111111114", "12", "2026");
|
||||
$this->assertEquals('200', $this->charge_response->status_code);
|
||||
}
|
||||
|
||||
public function testCardToken()
|
||||
{
|
||||
$this->charge_response = CoreApi::cardToken("4811111111111114", "12", "2026", "123");
|
||||
$this->assertEquals('200', $this->charge_response->status_code);
|
||||
}
|
||||
|
||||
public function testCardPointInquiry()
|
||||
{
|
||||
$this->charge_response = CoreApi::cardToken("4617006959746656", "12", "2026", "123");
|
||||
$cardPointResponse = CoreApi::cardPointInquiry($this->charge_response->token_id);
|
||||
$this->assertEquals('200', $cardPointResponse->status_code);
|
||||
}
|
||||
|
||||
public function testChargeCimbClicks()
|
||||
{
|
||||
$this->prepareChargeParams(
|
||||
'cimb_clicks',
|
||||
array(
|
||||
"description" => "Item Descriptions",
|
||||
)
|
||||
);
|
||||
$this->charge_response = CoreApi::charge($this->charge_params);
|
||||
$this->assertEquals('pending', $this->charge_response->transaction_status);
|
||||
$this->assertTrue(isset($this->charge_response->redirect_url));
|
||||
}
|
||||
|
||||
public function testChargePermataVa()
|
||||
{
|
||||
$this->prepareChargeParams(
|
||||
'bank_transfer',
|
||||
array(
|
||||
"bank" => "permata",
|
||||
)
|
||||
);
|
||||
$this->charge_response = CoreApi::charge($this->charge_params);
|
||||
$this->assertEquals('pending', $this->charge_response->transaction_status);
|
||||
$this->assertTrue(isset($this->charge_response->permata_va_number));
|
||||
}
|
||||
|
||||
public function testChargeEPayBri()
|
||||
{
|
||||
$this->prepareChargeParams('bri_epay');
|
||||
$this->charge_response = CoreApi::charge($this->charge_params);
|
||||
$this->assertEquals('pending', $this->charge_response->transaction_status);
|
||||
$this->assertTrue(isset($this->charge_response->redirect_url));
|
||||
}
|
||||
|
||||
public function testChargeMandiriBillPayment()
|
||||
{
|
||||
$this->prepareChargeParams(
|
||||
'echannel',
|
||||
array(
|
||||
"bill_info1" => "Payment for:",
|
||||
"bill_info2" => "Item descriptions",
|
||||
)
|
||||
);
|
||||
$this->charge_response = CoreApi::charge($this->charge_params);
|
||||
$this->assertEquals('pending', $this->charge_response->transaction_status);
|
||||
}
|
||||
|
||||
public function testChargeIndomaret()
|
||||
{
|
||||
$this->prepareChargeParams(
|
||||
'cstore',
|
||||
array(
|
||||
"store" => "indomaret",
|
||||
"message" => "Item descriptions",
|
||||
)
|
||||
);
|
||||
$this->charge_response = CoreApi::charge($this->charge_params);
|
||||
$this->assertEquals('pending', $this->charge_response->transaction_status);
|
||||
$this->assertTrue(isset($this->charge_response->payment_code));
|
||||
}
|
||||
|
||||
public function testChargeGopay()
|
||||
{
|
||||
$this->prepareChargeParams(
|
||||
'gopay',
|
||||
array(
|
||||
"enable_callback" => true,
|
||||
"callback_url" => "someapps://callback",
|
||||
)
|
||||
);
|
||||
$this->charge_response = CoreApi::charge($this->charge_params);
|
||||
$this->assertEquals('201', $this->charge_response->status_code);
|
||||
$this->assertEquals('pending', $this->charge_response->transaction_status);
|
||||
}
|
||||
|
||||
public function testCreateSubscription()
|
||||
{
|
||||
$param = array(
|
||||
"name" => "Monthly_2021",
|
||||
"amount" => "10000",
|
||||
"currency" => "IDR",
|
||||
"payment_type" => "credit_card",
|
||||
"token" => "dummy",
|
||||
"schedule" => array(
|
||||
"interval" => 1,
|
||||
"interval_unit" => "month",
|
||||
"max_interval" => "12",
|
||||
"start_time" => "2022-08-17 10:00:01 +0700"
|
||||
),
|
||||
"metadata" => array(
|
||||
"description" => "Recurring payment for user a"
|
||||
),
|
||||
"customer_details" => array(
|
||||
"first_name" => "John",
|
||||
"last_name" => "Doe",
|
||||
"email" => "johndoe@gmail.com",
|
||||
"phone_number" => "+628987654321"
|
||||
)
|
||||
);
|
||||
$this->charge_response = CoreApi::createSubscription($param);
|
||||
$this->assertEquals('active', $this->charge_response->status);
|
||||
$subscription_id = $this->charge_response->id;
|
||||
return $subscription_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateSubscription
|
||||
*/
|
||||
public function testGetSubscription($subscription_id)
|
||||
{
|
||||
$this->charge_response = CoreApi::getSubscription($subscription_id);
|
||||
$this->assertEquals('active', $this->charge_response->status);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateSubscription
|
||||
*/
|
||||
public function testDisableSubscription($subscription_id)
|
||||
{
|
||||
$this->charge_response = CoreApi::disableSubscription($subscription_id);
|
||||
$this->assertContains('Subscription is updated.', $this->charge_response->status_message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateSubscription
|
||||
*/
|
||||
public function testEnableSubscription($subscription_id)
|
||||
{
|
||||
$this->charge_response = CoreApi::enableSubscription($subscription_id);
|
||||
$this->assertContains('Subscription is updated.', $this->charge_response->status_message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateSubscription
|
||||
*/
|
||||
public function testUpdateSubscription($subscription_id)
|
||||
{
|
||||
$param = array(
|
||||
"name" => "Monthly_2021",
|
||||
"amount" => "25000",
|
||||
"currency" => "IDR",
|
||||
"token" => "dummy",
|
||||
"schedule" => array(
|
||||
"interval" => 1
|
||||
)
|
||||
);
|
||||
|
||||
$this->charge_response = CoreApi::updateSubscription($subscription_id, $param);
|
||||
$this->assertContains('Subscription is updated.', $this->charge_response->status_message);
|
||||
}
|
||||
|
||||
public function testGetSubscriptionWithNonExistAccount()
|
||||
{
|
||||
try {
|
||||
$this->charge_response = CoreApi::getSubscription("dummy");
|
||||
} catch (\Exception $e) {
|
||||
$this->assertContains("Midtrans API is returning API error.", $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function testDisableSubscriptionWithNonExistAccount()
|
||||
{
|
||||
try {
|
||||
$this->charge_response = CoreApi::disableSubscription("dummy");
|
||||
} catch (\Exception $e) {
|
||||
$this->assertContains("Midtrans API is returning API error.", $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function testEnableSubscriptionWithNonExistAccount()
|
||||
{
|
||||
try {
|
||||
$this->charge_response = CoreApi::enableSubscription("dummy");
|
||||
} catch (\Exception $e) {
|
||||
$this->assertContains("Midtrans API is returning API error.", $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function testUpdateSubscriptionWithNonExistAccount()
|
||||
{
|
||||
$param = array(
|
||||
"name" => "Monthly_2021",
|
||||
"amount" => "25000",
|
||||
"currency" => "IDR",
|
||||
"token" => "dummy",
|
||||
"schedule" => array(
|
||||
"interval" => 1
|
||||
)
|
||||
);
|
||||
|
||||
try {
|
||||
$this->charge_response = CoreApi::updateSubscription("dummy", $param);
|
||||
} catch (\Exception $e) {
|
||||
$this->assertContains("Midtrans API is returning API error.", $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function testCreatePayAccount()
|
||||
{
|
||||
$params = array(
|
||||
"payment_type" => "gopay",
|
||||
"gopay_partner" => array(
|
||||
"phone_number" => 874567446788,
|
||||
"redirect_url" => "https://www.google.com"
|
||||
)
|
||||
);
|
||||
$this->charge_response = CoreApi::linkPaymentAccount($params);
|
||||
$this->assertEquals('201', $this->charge_response->status_code);
|
||||
$this->assertEquals('PENDING', $this->charge_response->account_status);
|
||||
$account_id = $this->charge_response->account_id;
|
||||
return $account_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreatePayAccount
|
||||
*/
|
||||
public function testGetPaymentAccount($account_id)
|
||||
{
|
||||
$this->charge_response = CoreApi::getPaymentAccount($account_id);
|
||||
$this->assertEquals('201', $this->charge_response->status_code);
|
||||
$this->assertEquals('PENDING', $this->charge_response->account_status);
|
||||
}
|
||||
|
||||
|
||||
public function testGetPaymentAccountWithNonExistAccount()
|
||||
{
|
||||
try {
|
||||
$this->charge_response = CoreApi::getPaymentAccount("dummy");
|
||||
} catch (\Exception $e) {
|
||||
$this->assertContains("Midtrans API is returning API error.", $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public function testUnlinkPaymentAccountWithNonExistAccount()
|
||||
{
|
||||
try {
|
||||
$this->charge_response = CoreApi::unlinkPaymentAccount("dummy");
|
||||
} catch (\Exception $e) {
|
||||
$this->assertContains("Account doesn't exist.", $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Midtrans\integration;
|
||||
|
||||
use Midtrans\Config;
|
||||
|
||||
abstract class IntegrationTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public static function setUpBeforeClass()
|
||||
{
|
||||
Config::$serverKey = getenv('SERVER_KEY');
|
||||
Config::$clientKey = getenv('CLIENT_KEY');
|
||||
Config::$isProduction = false;
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
// One second interval to avoid throttle
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Midtrans\integration;
|
||||
|
||||
use Midtrans\CoreApi;
|
||||
use Midtrans\Notification;
|
||||
use Midtrans\Transaction;
|
||||
use Midtrans\utility\MtChargeFixture;
|
||||
|
||||
require_once 'IntegrationTest.php';
|
||||
|
||||
|
||||
class NotificationIntegrationTest extends IntegrationTest
|
||||
{
|
||||
private $status_response;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$charge_params = MtChargeFixture::build('bri_epay');
|
||||
$charge_response = CoreApi::charge($charge_params);
|
||||
$this->status_response = Transaction::status($charge_response->transaction_id);
|
||||
}
|
||||
|
||||
public function testValidBriEPayNotification()
|
||||
{
|
||||
// Assume status response is similar to HTTP(s) notification
|
||||
$tmpfname = tempnam(sys_get_temp_dir(), "test");
|
||||
file_put_contents($tmpfname, json_encode($this->status_response));
|
||||
|
||||
$notif = new Notification($tmpfname);
|
||||
|
||||
$this->assertEquals($notif->status_code, "201");
|
||||
$this->assertEquals($notif->transaction_status, "pending");
|
||||
$this->assertEquals($notif->payment_type, "bri_epay");
|
||||
$this->assertEquals($notif->order_id, $this->status_response->order_id);
|
||||
$this->assertEquals($notif->transaction_id, $this->status_response->transaction_id);
|
||||
$this->assertEquals($notif->gross_amount, $this->status_response->gross_amount);
|
||||
|
||||
unlink($tmpfname);
|
||||
}
|
||||
|
||||
public function testFraudulentBriEPayNotification()
|
||||
{
|
||||
/*
|
||||
As a fraudster, I want the merchant to think that I finished e-Pay BRI payment
|
||||
1. alter transaction status, from pending to settlement
|
||||
2. alter status code, from 201 to 200
|
||||
*/
|
||||
$this->status_response->transaction_status = "settlement";
|
||||
$this->status_response->status_code = "200";
|
||||
|
||||
$tmpfname = tempnam(sys_get_temp_dir(), "test");
|
||||
file_put_contents($tmpfname, json_encode($this->status_response));
|
||||
|
||||
$notif = new Notification($tmpfname);
|
||||
|
||||
/*
|
||||
Merchant should not be tricked... thanks to Get Status API
|
||||
*/
|
||||
$this->assertEquals("201", $notif->status_code);
|
||||
$this->assertEquals("pending", $notif->transaction_status);
|
||||
$this->assertEquals("bri_epay", $notif->payment_type);
|
||||
$this->assertEquals($this->status_response->order_id, $notif->order_id);
|
||||
$this->assertEquals($this->status_response->transaction_id, $notif->transaction_id);
|
||||
$this->assertEquals($this->status_response->gross_amount, $notif->gross_amount);
|
||||
|
||||
unlink($tmpfname);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Midtrans\integration;
|
||||
|
||||
use Midtrans\Snap;
|
||||
use Midtrans\utility\MtChargeFixture;
|
||||
|
||||
require_once 'IntegrationTest.php';
|
||||
|
||||
class SnapIntegrationTest extends IntegrationTest
|
||||
{
|
||||
public function testSnapToken()
|
||||
{
|
||||
$charge_params = MtChargeFixture::build('vtweb');
|
||||
$token_id = Snap::getSnapToken($charge_params);
|
||||
|
||||
$this->assertTrue(isset($token_id));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Midtrans\integration;
|
||||
|
||||
use Midtrans\CoreApi;
|
||||
use Midtrans\Transaction;
|
||||
use Midtrans\utility\MtChargeFixture;
|
||||
|
||||
require_once 'IntegrationTest.php';
|
||||
|
||||
class TransactionIntegrationTest extends IntegrationTest
|
||||
{
|
||||
|
||||
public function testStatusPermataVa()
|
||||
{
|
||||
$charge_params = MtChargeFixture::build(
|
||||
'bank_transfer',
|
||||
array(
|
||||
"bank" => "permata",
|
||||
)
|
||||
);
|
||||
$charge_response = CoreApi::charge($charge_params);
|
||||
$status_response = Transaction::status($charge_response->transaction_id);
|
||||
|
||||
$this->assertEquals('201', $status_response->status_code);
|
||||
$this->assertEquals('pending', $status_response->transaction_status);
|
||||
$this->assertEquals($charge_params['transaction_details']['order_id'], $status_response->order_id);
|
||||
$this->assertEquals($charge_params['transaction_details']['gross_amount'], $status_response->gross_amount);
|
||||
$this->assertEquals($charge_response->transaction_id, $status_response->transaction_id);
|
||||
$this->assertEquals($charge_response->transaction_time, $status_response->transaction_time);
|
||||
$this->assertEquals('Success, transaction is found', $status_response->status_message);
|
||||
|
||||
$this->assertTrue(isset($status_response->signature_key));
|
||||
}
|
||||
|
||||
public function testCancelPermataVa()
|
||||
{
|
||||
$charge_params = MtChargeFixture::build(
|
||||
'bank_transfer',
|
||||
array(
|
||||
"bank" => "permata",
|
||||
)
|
||||
);
|
||||
$charge_response = CoreApi::charge($charge_params);
|
||||
$cancel_status_code = Transaction::cancel($charge_response->transaction_id);
|
||||
|
||||
$this->assertEquals('200', $cancel_status_code);
|
||||
}
|
||||
|
||||
public function testExpirePermataVa()
|
||||
{
|
||||
$charge_params = MtChargeFixture::build(
|
||||
'bank_transfer',
|
||||
array(
|
||||
"bank" => "permata",
|
||||
)
|
||||
);
|
||||
$charge_response = CoreApi::charge($charge_params);
|
||||
$expire = Transaction::expire($charge_response->transaction_id);
|
||||
|
||||
$this->assertEquals('407', $expire->status_code);
|
||||
|
||||
// Verify transaction via API
|
||||
$txn_status = Transaction::status($charge_response->transaction_id);
|
||||
$this->assertEquals("407", $txn_status->status_code);
|
||||
$this->assertEquals("expire", $txn_status->transaction_status);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Midtrans\utility;
|
||||
|
||||
class MtFixture
|
||||
{
|
||||
protected static function readFixture($filename)
|
||||
{
|
||||
$charge_json = file_get_contents(__DIR__ . '/fixture/' . $filename);
|
||||
$charge_template_params = json_decode($charge_json, true);
|
||||
$charge_template_params['transaction_details']['order_id'] = rand();
|
||||
return $charge_template_params;
|
||||
}
|
||||
}
|
||||
|
||||
class MtChargeFixture extends MtFixture
|
||||
{
|
||||
public static function build($payment_type, $payment_data = null)
|
||||
{
|
||||
$charge_params = self::readFixture('mt_charge.json');
|
||||
|
||||
if (!is_null($payment_type)) {
|
||||
$charge_params['payment_type'] = $payment_type;
|
||||
}
|
||||
|
||||
if (!is_null($payment_data)) {
|
||||
$charge_params[$payment_type] = $payment_data;
|
||||
}
|
||||
return $charge_params;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"transaction_details": {
|
||||
"order_id": "C17550",
|
||||
"gross_amount": 145000
|
||||
},
|
||||
|
||||
"item_details" : [
|
||||
{
|
||||
"id": "a1",
|
||||
"price": 50000,
|
||||
"quantity": 2,
|
||||
"name": "Apel"
|
||||
},
|
||||
{
|
||||
"id": "a2",
|
||||
"price": 45000,
|
||||
"quantity": 1,
|
||||
"name": "Jeruk"
|
||||
}
|
||||
],
|
||||
"customer_details": {
|
||||
"first_name": "Andri",
|
||||
"last_name": "Litani",
|
||||
"email": "andri@litani.com",
|
||||
"phone": "081122334455",
|
||||
"billing_address": {
|
||||
"first_name": "Andri",
|
||||
"last_name": "Litani",
|
||||
"address": "Mangga 20",
|
||||
"city": "Jakarta",
|
||||
"postal_code": "16602",
|
||||
"phone": "081122334455",
|
||||
"country_code": "IDN"
|
||||
},
|
||||
"shipping_address": {
|
||||
"first_name": "Obet",
|
||||
"last_name": "Supriadi",
|
||||
"address": "Manggis 90",
|
||||
"city": "Jakarta",
|
||||
"postal_code": "16601",
|
||||
"phone": "08113366345",
|
||||
"country_code": "IDN"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user