diff --git a/MAINTAINING.md b/MAINTAINING.md
index 0a7c301..303fda5 100644
--- a/MAINTAINING.md
+++ b/MAINTAINING.md
@@ -28,6 +28,11 @@ Other:
- External WC PG dev guide: https://www.skyverge.com/blog/how-to-create-a-simple-woocommerce-payment-gateway/
- WP get_options() functions: https://developer.wordpress.org/reference/functions/get_option/
+### Attention
+- Due to the feature of "custom order_id suffix to prevent duplicated order_id", order_id input and output may need to be handled non-traditionally, look for `@TAG: order-suffix-separator` in the code comments. e.g:
+ - when sending order_id to Midtrans, it may need to go thru func `WC_Midtrans_Utils::generate_non_duplicate_order_id`
+ - when receiving order_id, it may need to go thru func `WC_Midtrans_Utils::check_and_restore_original_order_id`
+
### Separted Payment Buttons
To implement separated payment buttons (separate WC payment gateway) for each of Midtrans' supported payment methods, the following implementations are made:
- within `/class/sub-specific-buttons` those are the class files
diff --git a/README.md b/README.md
index 0c470f3..d1dea89 100644
--- a/README.md
+++ b/README.md
@@ -8,21 +8,23 @@ Also [Available on Wordpress plugin store](https://wordpress.org/plugins/midtran
### Description
-This plugin will allow secure online payment on your WooCommerce store, without your customer ever need to leave your WooCommerce store! With beautiful responsive payment interface built-in.
-Midtrans is an online payment gateway. They strive to make payments simple for both the merchant and customers.
-Support various online payment channel.
-Support WooCommerce v3 & v2.
+This plugin will allow secure online payment on your WooCommerce store, without your customer ever need to leave your WooCommerce store!
+
+Midtrans-WooCommerce is official plugin from [Midtrans](https://midtrans.com). Midtrans is an online payment gateway. We strive to make payments simple & secure for both the merchant and customers. Support various online payment channel. Support WooCommerce v3 & v2.
+
+Please follow [this step by step guide](https://docs.midtrans.com/en/snap/with-plugins?id=wordpress-woocommerce) for complete configuration. If you have any feedback or request, please [do let us know here](https://docs.midtrans.com/en/snap/with-plugins?id=feedback-and-request).
Payment Method Feature:
* Credit card fullpayment and other payment methods.
-* Bank transfer, internet banking for various banks
+* E-wallet, Bank transfer, internet banking for various banks
* Credit card Online & offline installment payment.
* Credit card BIN, bank transfer, and other channel promo payment.
* Credit card MIGS acquiring channel.
* Custom expiry.
* Two-click & One-click feature.
-* Midtrans Snap all payment method fullpayment.
+* Midtrans Snap all supported payment method.
+* Optional: Separated specific payment buttons with its own icons.
### Installation
@@ -89,7 +91,7 @@ You can customize icon that will be shown on payment buttons, from the plugin co
All available values for the field:
```
-credit_card.png, gopay.png, shopeepay.png, qris.png, other_va.png, bni_va.png, bri_va.png, bca_va.png, permata_va.png, echannel.png, alfamart.png, indomaret.png, akulaku.png, bca_klikpay.png, cimb_clicks.png, danamon_online.png, midtrans.png
+midtrans.png, credit_card.png, gopay.png, shopeepay.png, qris.png, other_va.png, bni_va.png, bri_va.png, bca_va.png, permata_va.png, echannel.png, alfamart.png, indomaret.png, akulaku.png, bca_klikpay.png, cimb_clicks.png, danamon_online.png
```
Or refer to [payment-methods folder](/public/images/payment-methods) to see the list of all available file names. The image file will be loaded from that folder.
@@ -136,11 +138,13 @@ If you are a developer or know how to customize Wordpress, this section may be u
This plugin have few available [WP hooks](https://developer.wordpress.org/plugins/hooks/):
- filter: `midtrans_snap_params_main_before_charge` (1 params)
- - For if you want to modify Snap API JSON param on the main gateway, before transaction is created on Midtrans side.
+ - For if you want to modify Snap API JSON param on the main gateway, before transaction is created on Midtrans side. The $params is PHP Array representation of [Snap API JSON param](https://snap-docs.midtrans.com/#request-body-json-parameter)
- action: `midtrans_after_notification_payment_complete` (2 params)
- For if you want to perform action/update WC Order object when the payment is declared as complete upon Midtrans notification received.
- action: `midtrans_on_notification_received` (2 params)
- For if you want to perform action/update WC Order object upon Midtrans notification received.
+- action: `midtrans-handle-valid-notification` (1 params)
+ - For if you want to perform something upon valid Midtrans notification received. Note: this is legacy hook, better use the hook above.
Example implementation:
```php
@@ -174,6 +178,8 @@ function my_midtrans_on_notif_hook( $order, $midtrans_notification ) {
For reference on where/which file to apply that code example, [refer here](https://blog.nexcess.net/the-right-way-to-add-custom-functions-to-your-wordpress-site/).
+Note: for `midtrans_after_notification_payment_complete` & `midtrans_on_notification_received` hooks, if you are using [custom "WC Order Status on Payment Paid"](https://docs.midtrans.com/en/snap/with-plugins?id=advanced-customize-woocommerce-order-status-upon-payment-paid) config, the final WC Order status value can get overridden by that config. As that config is executed last.
+
#### Customizing Snap API parameters
diff --git a/abstract/abstract.midtrans-gateway.php b/abstract/abstract.midtrans-gateway.php
index 67da586..8beb199 100644
--- a/abstract/abstract.midtrans-gateway.php
+++ b/abstract/abstract.midtrans-gateway.php
@@ -131,16 +131,26 @@ public function process_refund($order_id, $amount = null, $reason = '') {
*/
public function refund( $order, $order_id, $amount, $reason ) {
$refund_params = array(
+ // @TODO: careful with this order_id here, which does not get deduplicated treatment
'refund_key' => 'RefundID' . $order_id . '-' . current_time('timestamp'),
'amount' => $amount,
'reason' => $reason
);
try {
- $response = WC_Midtrans_API::createRefund($order_id, $refund_params, $this->id);
+ if(strpos($this->id, 'midtrans_sub') !== false){
+ // for sub separated gateway buttons, use main gateway plugin id instead
+ $this->id = 'midtrans';
+ }
+ // @TODO: call refund API with transaction_id instead of order_id to avoid id not found for suffixed order_id. $order->get_transaction_id();
+ $transaction_id = $order->get_transaction_id()
+ ? $order->get_transaction_id()
+ : $order_id;
+ $response = WC_Midtrans_API::createRefund($transaction_id, $refund_params, $this->id);
} catch (Exception $e) {
$this->setLogError( $e->getMessage() );
- $error_message = strpos($e->getMessage(), '412') ? $e->getMessage() . ' Note: Refund via Midtrans only for specific payment method, please consult to your midtrans PIC for more information' : $e->getMessage();
+ // error_log(var_export($e,1));
+ $error_message = strpos($e->getMessage(), '412') ? $e->getMessage() . ' Note: Refund via Midtrans API only available on some payment methods, and if the payment status is eligible. Please consult to your midtrans PIC for more information' : $e->getMessage();
return $error_message;
}
@@ -334,6 +344,7 @@ public function getResponseTemplate( $order ) {
* @return WC_Order_Refund|WP_Error
*/
public function midtrans_refund( $order_id, $refund_amount, $refund_reason, $isFullRefund = false ) {
+ $order_id = WC_Midtrans_Utils::check_and_restore_original_order_id();
$order = wc_get_order( $order_id );
if( ! is_a( $order, 'WC_Order') ) {
return;
@@ -386,6 +397,20 @@ public function midtrans_refund( $order_id, $refund_amount, $refund_reason, $isF
return $refund;
}
+ /**
+ * Custom helper function to set customer web session cookies of WC order's finish_url
+ * That will be used by finish url handler to redirect customer to upon finish url is reached
+ * Cookies is used to strictly allow only the transacting-customer
+ * to access the order's finish url
+ * @TAG: finish_url_user_cookies
+ * @param WC_Order $order WC Order instance of the current transaction
+ */
+ public function set_finish_url_user_cookies( $order ) {
+ $cookie_name = 'wc_midtrans_last_order_finish_url';
+ $order_finish_url = $order->get_checkout_order_received_url();
+ setcookie($cookie_name, $order_finish_url);
+ }
+
/**
* Custom helper function to write messages to WP/WC error log.
* @TODO: refactor name to make it more descriptive?
diff --git a/class/class.midtrans-gateway-api.php b/class/class.midtrans-gateway-api.php
index cb44afd..7b9ab9a 100644
--- a/class/class.midtrans-gateway-api.php
+++ b/class/class.midtrans-gateway-api.php
@@ -87,12 +87,47 @@ public static function get_environment() {
* @return void
*/
public static function fetchAndSetMidtransApiConfig( $plugin_id="midtrans" ) {
+ if(strpos($plugin_id, 'midtrans_sub') !== false){
+ // for sub separated gateway buttons, use main gateway plugin id instead
+ $plugin_id = 'midtrans';
+ }
self::fetchAndSetCurrentPluginOptions( $plugin_id );
Midtrans\Config::$isProduction = (self::get_environment() == 'production') ? true : false;
Midtrans\Config::$serverKey = self::get_server_key();
Midtrans\Config::$isSanitized = true;
}
+ /**
+ * Same as createSnapTransaction, but it will auto handle exception
+ * 406 duplicated order_id exception from Snap API, by calling WC_Midtrans_Utils::generate_non_duplicate_order_id
+ * @param object $order the WC Order instance.
+ * @param array $params Payment options.
+ * @param string $plugin_id ID of the plugin class calling this function
+ * @return object Snap response (token and redirect_url).
+ * @throws Exception curl error or midtrans error.
+ */
+ public static function createSnapTransactionHandleDuplicate( $order, $params, $plugin_id="midtrans") {
+ try {
+ $response = self::createSnapTransaction($params, $plugin_id);
+ } catch (Exception $e) {
+ // Handle: Snap order_id duplicated, retry with suffixed order_id
+ if( strpos($e->getMessage(), 'transaction_details.order_id sudah digunakan') !== false) {
+ self::setLogRequest( $e->getMessage().' - Attempt to auto retry with suffixed order_id', $plugin_id );
+ // @TAG: order-id-suffix-handling
+ $params['transaction_details']['order_id'] =
+ WC_Midtrans_Utils::generate_non_duplicate_order_id($params['transaction_details']['order_id']);
+ $response = self::createSnapTransaction($params, $plugin_id);
+
+ // store the suffixed order id to order metadata
+ // @TAG: order-id-suffix-handling-meta
+ $order->update_meta_data('_mt_suffixed_midtrans_order_id', $params['transaction_details']['order_id']);
+ } else {
+ throw $e;
+ }
+ }
+ return $response;
+ }
+
/**
* Create Snap Token.
* @param array $params Payment options.
diff --git a/class/class.midtrans-gateway-installment.php b/class/class.midtrans-gateway-installment.php
index 864450c..52c116d 100755
--- a/class/class.midtrans-gateway-installment.php
+++ b/class/class.midtrans-gateway-installment.php
@@ -88,7 +88,7 @@ function process_payment( $order_id ) {
// Empty the cart because payment is initiated.
$woocommerce->cart->empty_cart();
try {
- $snapResponse = WC_Midtrans_API::createSnapTransaction( $params, $this->id );
+ $snapResponse = WC_Midtrans_API::createSnapTransactionHandleDuplicate( $order, $params, $this->id );
} catch (Exception $e) {
$this->setLogError( $e->getMessage() );
WC_Midtrans_Utils::json_print_exception( $e, $this );
@@ -105,7 +105,8 @@ function process_payment( $order_id ) {
$order->update_meta_data('_mt_payment_snap_token',$snapResponse->token);
$order->update_meta_data('_mt_payment_url',$snapResponse->redirect_url);
$order->save();
-
+ // set wc order's finish_url on user's session cookie
+ $this->set_finish_url_user_cookies($order);
if(property_exists($this,'enable_immediate_reduce_stock') && $this->enable_immediate_reduce_stock == 'yes'){
wc_reduce_stock_levels($order);
}
diff --git a/class/class.midtrans-gateway-installmentoff.php b/class/class.midtrans-gateway-installmentoff.php
index 9f9e3d6..b1bd030 100755
--- a/class/class.midtrans-gateway-installmentoff.php
+++ b/class/class.midtrans-gateway-installmentoff.php
@@ -117,7 +117,7 @@ public function process_payment( $order_id ) {
// Empty the cart because payment is initiated.
$woocommerce->cart->empty_cart();
try {
- $snapResponse = WC_Midtrans_API::createSnapTransaction( $params, $this->id );
+ $snapResponse = WC_Midtrans_API::createSnapTransactionHandleDuplicate( $order, $params, $this->id );
} catch (Exception $e) {
$this->setLogError( $e->getMessage() );
WC_Midtrans_Utils::json_print_exception( $e, $this );
@@ -135,6 +135,8 @@ public function process_payment( $order_id ) {
$order->update_meta_data('_mt_payment_snap_token',$snapResponse->token);
$order->update_meta_data('_mt_payment_url',$snapResponse->redirect_url);
$order->save();
+ // set wc order's finish_url on user's session cookie
+ $this->set_finish_url_user_cookies($order);
if(property_exists($this,'enable_immediate_reduce_stock') && $this->enable_immediate_reduce_stock == 'yes'){
// Reduce item stock on WC, item also auto reduced on order `pending` status changes
wc_reduce_stock_levels($order);
diff --git a/class/class.midtrans-gateway-notif-handler.php b/class/class.midtrans-gateway-notif-handler.php
index 176a6a9..37040bd 100644
--- a/class/class.midtrans-gateway-notif-handler.php
+++ b/class/class.midtrans-gateway-notif-handler.php
@@ -50,6 +50,21 @@ public function doEarlyAckResponse() {
return $raw_notification;
}
+ /**
+ * Redirect transacting-user to the finish url set when they were checking out
+ * if they were the authorized transacting-user, they will have the finish_url on cookie
+ * @TAG: finish_url_user_cookies
+ */
+ public function checkAndRedirectUserToFinishUrl(){
+ if(isset($_COOKIE['wc_midtrans_last_order_finish_url'])){
+ // authorized transacting-user
+ wp_redirect($_COOKIE['wc_midtrans_last_order_finish_url']);
+ }else{
+ // else, unauthorized user, redirect to shop homepage by default.
+ wp_redirect( get_permalink( wc_get_page_id( 'shop' ) ) );
+ }
+ }
+
/**
* getPluginOptions
* @param string $plugin_id plugin id of the paid order
@@ -85,11 +100,19 @@ public function handleMidtransNotificationRequest() {
$sanitizedPost['response'] =
isset($_POST['response'])? sanitize_text_field($_POST['response']): null;
+ // @TAG: order-id-suffix-handling
+ $sanitized['order_id'] =
+ WC_Midtrans_Utils::check_and_restore_original_order_id($sanitized['order_id']);
+
// check whether the request is POST or GET,
// @TODO: refactor this conditions, this doesn't quite represent conditions for a POST request
if(empty($sanitized['order_id']) && empty($sanitizedPost['id']) && empty($sanitized['id']) && empty($sanitizedPost['response'])) {
// Request is POST, proceed to create new notification, then update the payment status
$raw_notification = $this->doEarlyAckResponse();
+
+ // @TAG: order-id-suffix-handling
+ $raw_notification['order_id'] =
+ WC_Midtrans_Utils::check_and_restore_original_order_id($raw_notification['order_id']);
// Get WooCommerce order
$wcorder = wc_get_order( $raw_notification['order_id'] );
// exit if the order id doesn't exist in WooCommerce dashboard
@@ -107,7 +130,11 @@ public function handleMidtransNotificationRequest() {
$midtrans_notification = WC_Midtrans_API::getStatusFromMidtransNotif( $plugin_id );
// If notification verified, handle it
if (in_array($midtrans_notification->status_code, array(200, 201, 202, 407))) {
- if (wc_get_order($midtrans_notification->order_id) != false) {
+ // @TAG: order-id-suffix-handling
+ $order_id =
+ WC_Midtrans_Utils::check_and_restore_original_order_id($midtrans_notification->order_id);
+ // @TODO: relocate this check into the function itself, to prevent unnecessary double DB query load
+ if (wc_get_order($order_id) != false) {
do_action( "midtrans-handle-valid-notification", $midtrans_notification, $plugin_id );
}
}
@@ -126,23 +153,24 @@ public function handleMidtransNotificationRequest() {
if( !empty($sanitized['order_id']) && !empty($sanitized['status_code']) && $sanitized['status_code'] <= 200) {
$order_id = $sanitized['order_id'];
// error_log($this->get_return_url( $order )); //debug
- $order = new WC_Order( $order_id );
- wp_redirect($order->get_checkout_order_received_url());
+ $this->checkAndRedirectUserToFinishUrl();
}
// if or pending/challenge
else if( !empty($sanitized['order_id']) && !empty($sanitized['transaction_status']) && $sanitized['status_code'] == 201) {
- $order_id = $sanitized['order_id'];
- $order = new WC_Order( $order_id );
- $plugin_id = $order->get_payment_method();
+ try {
+ $order_id = $sanitized['order_id'];
+ $order = new WC_Order( $order_id );
+ $plugin_id = $order->get_payment_method();
- $plugin_options = $this->getPluginOptions($plugin_id);
- if( array_key_exists('ignore_pending_status',$plugin_options)
- && $plugin_options['ignore_pending_status'] == 'yes'
- ){
- wp_redirect( get_permalink( wc_get_page_id( 'shop' ) ) );
- exit;
- }
- wp_redirect($order->get_checkout_order_received_url());
+ $plugin_options = $this->getPluginOptions($plugin_id);
+ if( array_key_exists('ignore_pending_status',$plugin_options)
+ && $plugin_options['ignore_pending_status'] == 'yes'
+ ){
+ wp_redirect( get_permalink( wc_get_page_id( 'shop' ) ) );
+ exit;
+ }
+ } catch (Exception $e) { } // catch if order not exist on WC
+ $this->checkAndRedirectUserToFinishUrl();
}
//if deny, redirect to order checkout page again
else if( !empty($sanitized['order_id']) && !empty($sanitized['transaction_status']) && $sanitized['status_code'] >= 202){
@@ -154,10 +182,14 @@ public function handleMidtransNotificationRequest() {
// if customer redirected from async payment with POST `response` (CIMB clicks, etc)
} else if ( !empty($sanitizedPost['response']) ){
$responses = json_decode( stripslashes($sanitizedPost['response']), true);
+
+ // @TAG: order-id-suffix-handling
+ $responses['order_id'] =
+ WC_Midtrans_Utils::check_and_restore_original_order_id($responses['order_id']);
$order = new WC_Order( $responses['order_id'] );
// if async payment paid
if ( $responses['status_code'] == 200) {
- wp_redirect($order->get_checkout_order_received_url());
+ $this->checkAndRedirectUserToFinishUrl();
}
// if async payment not paid
else {
@@ -177,11 +209,14 @@ public function handleMidtransNotificationRequest() {
// But actually, BCA Klikpay already handled on finish-url-page.php, evaluate if this still needed
$plugin_id = wc_get_order( $sanitized['id'] )->get_payment_method();
$midtrans_notification = WC_Midtrans_API::getMidtransStatus($id, $plugin_id);
- $order_id = $midtrans_notification->order_id;
+
+ // @TODO remove this order_id? seems unused
+ // @TAG: order-id-suffix-handling
+ $order_id =
+ WC_Midtrans_Utils::check_and_restore_original_order_id($midtrans_notification->order_id);
// if async payment paid
if ($midtrans_notification->transaction_status == 'settlement'){
- $order = new WC_Order( $order_id );
- wp_redirect($order->get_checkout_order_received_url());
+ $this->checkAndRedirectUserToFinishUrl();
}
// if async payment not paid
else {
@@ -204,10 +239,10 @@ public function handleMidtransNotificationRequest() {
*/
public function handleMidtransValidNotificationRequest( $midtrans_notification, $plugin_id = 'midtrans' ) {
global $woocommerce;
-
- $order = new WC_Order( $midtrans_notification->order_id );
+ // @TAG: order-id-suffix-handling
+ $order_id = WC_Midtrans_Utils::check_and_restore_original_order_id($midtrans_notification->order_id);
+ $order = new WC_Order( $order_id );
$order->add_order_note(__('Midtrans HTTP notification received: '.$midtrans_notification->transaction_status.'. Midtrans-'.$midtrans_notification->payment_type,'midtrans-woocommerce'));
- $order_id = $midtrans_notification->order_id;
// allow merchant-defined custom action function to perform action on $order upon notif handling
do_action( 'midtrans_on_notification_received', $order, $midtrans_notification );
@@ -294,8 +329,11 @@ public function validateRefundNotif( $midtrans_notification ) {
}
$refund_request = $midtrans_notification->refunds[$lastArrayIndex];
+ // @TAG: order-id-suffix-handling
+ $order_id =
+ WC_Midtrans_Utils::check_and_restore_original_order_id($midtrans_notification->order_id);
// Validate the refund doesn't charge twice by the refund last index
- $order_notes = wc_get_order_notes(array('order_id' => $midtrans_notification->order_id));
+ $order_notes = wc_get_order_notes(array('order_id' => $order_id));
foreach($order_notes as $value) {
if (strpos($value->content, $refund_request->refund_key ) !== false) {
return false;
@@ -312,10 +350,13 @@ public function validateRefundNotif( $midtrans_notification ) {
* @return void
*/
public function checkAndHandleWCSubscriptionTxnNotif( $midtrans_notification, $order ) {
+ // @TAG: order-id-suffix-handling
+ $order_id =
+ WC_Midtrans_Utils::check_and_restore_original_order_id($midtrans_notification->order_id);
// Process if this is a subscription transaction
- if ( wcs_order_contains_subscription( $midtrans_notification->order_id ) || wcs_is_subscription( $midtrans_notification->order_id ) || wcs_order_contains_renewal( $midtrans_notification->order_id ) ) {
+ if ( wcs_order_contains_subscription( $order_id ) || wcs_is_subscription( $order_id ) || wcs_order_contains_renewal( $order_id ) ) {
// if not subscription and wc status pending, don't process (because that's a recurring transaction)
- if ( wcs_order_contains_renewal( $midtrans_notification->order_id) && $order->get_status() == 'pending' ) {
+ if ( wcs_order_contains_renewal( $order_id) && $order->get_status() == 'pending' ) {
return false;
}
$subscriptions = wcs_get_subscriptions_for_order( $order, array( 'order_type' => 'any' ) );
diff --git a/class/class.midtrans-gateway-paymentrequest.php b/class/class.midtrans-gateway-paymentrequest.php
index afb6288..e9573d9 100755
--- a/class/class.midtrans-gateway-paymentrequest.php
+++ b/class/class.midtrans-gateway-paymentrequest.php
@@ -81,7 +81,7 @@ function process_payment( $order_id ) {
// Empty the cart because payment is initiated.
$woocommerce->cart->empty_cart();
try {
- $snapResponse = WC_Midtrans_API::createSnapTransaction( $params, $this->id );
+ $snapResponse = WC_Midtrans_API::createSnapTransactionHandleDuplicate( $order, $params, $this->id );
} catch (Exception $e) {
$this->setLogError( $e->getMessage() );
WC_Midtrans_Utils::json_print_exception( $e, $this );
@@ -98,6 +98,8 @@ function process_payment( $order_id ) {
$order->update_meta_data('_mt_payment_snap_token',$snapResponse->token);
$order->update_meta_data('_mt_payment_url',$snapResponse->redirect_url);
$order->save();
+ // set wc order's finish_url on user's session cookie
+ $this->set_finish_url_user_cookies($order);
if(property_exists($this,'enable_immediate_reduce_stock') && $this->enable_immediate_reduce_stock == 'yes'){
wc_reduce_stock_levels($order);
}
diff --git a/class/class.midtrans-gateway-promo.php b/class/class.midtrans-gateway-promo.php
index 6ffc717..df9178c 100755
--- a/class/class.midtrans-gateway-promo.php
+++ b/class/class.midtrans-gateway-promo.php
@@ -112,7 +112,7 @@ function process_payment( $order_id ) {
$woocommerce->cart->empty_cart();
try {
- $snapResponse = WC_Midtrans_API::createSnapTransaction( $params, $this->id );
+ $snapResponse = WC_Midtrans_API::createSnapTransactionHandleDuplicate( $order, $params, $this->id );
} catch (Exception $e) {
$this->setLogError( $e->getMessage() );
WC_Midtrans_Utils::json_print_exception( $e, $this );
@@ -130,6 +130,9 @@ function process_payment( $order_id ) {
$order->update_meta_data('_mt_payment_url',$snapResponse->redirect_url);
$order->save();
+ // set wc order's finish_url on user's session cookie
+ $this->set_finish_url_user_cookies($order);
+
if(property_exists($this,'enable_immediate_reduce_stock') && $this->enable_immediate_reduce_stock == 'yes'){
wc_reduce_stock_levels($order);
}
diff --git a/class/class.midtrans-gateway-subscription.php b/class/class.midtrans-gateway-subscription.php
index 4941837..096f28b 100644
--- a/class/class.midtrans-gateway-subscription.php
+++ b/class/class.midtrans-gateway-subscription.php
@@ -109,7 +109,7 @@ function process_payment( $order_id ) {
// Empty the cart because payment is initiated.
$woocommerce->cart->empty_cart();
try {
- $snapResponse = WC_Midtrans_API::createSnapTransaction( $params, $this->id );
+ $snapResponse = WC_Midtrans_API::createSnapTransactionHandleDuplicate( $order, $params, $this->id );
} catch (Exception $e) {
$this->setLogError( $e->getMessage() );
WC_Midtrans_Utils::json_print_exception( $e, $this );
diff --git a/class/class.midtrans-gateway.php b/class/class.midtrans-gateway.php
index 1299592..9d96737 100755
--- a/class/class.midtrans-gateway.php
+++ b/class/class.midtrans-gateway.php
@@ -132,7 +132,7 @@ public function process_payment_helper( $order_id, $options = false ) {
// allow merchant-defined custom filter function to modify snap $params
$params = apply_filters( 'midtrans_snap_params_main_before_charge', $params );
try {
- $snapResponse = WC_Midtrans_API::createSnapTransaction( $params, $this->id );
+ $snapResponse = WC_Midtrans_API::createSnapTransactionHandleDuplicate( $order, $params, $this->id );
} catch (Exception $e) {
$this->setLogError( $e->getMessage() );
WC_Midtrans_Utils::json_print_exception( $e, $this );
@@ -151,6 +151,9 @@ public function process_payment_helper( $order_id, $options = false ) {
$order->update_meta_data('_mt_payment_url',$snapResponse->redirect_url);
$order->save();
+ // set wc order's finish_url on user's session cookie
+ $this->set_finish_url_user_cookies($order);
+
// @TODO: default to yes or remove this options: enable_immediate_reduce_stock
if(property_exists($this,'enable_immediate_reduce_stock') && $this->enable_immediate_reduce_stock == 'yes'){
// Reduce item stock on WC, item also auto reduced on order `pending` status changes
@@ -196,7 +199,7 @@ protected function getDefaultTitle () {
* @return string
*/
protected function getSettingsDescription() {
- return __('Secure payment via Midtrans that accept various payment methods, with mobile friendly built-in interface, or (optionally) redirection. This is the main payment button, 1 single button for multiple available payments methods. Please follow "how-to configure guide" here.', 'midtrans-woocommerce');
+ return __('Secure payment via Midtrans that accept various payment methods, with mobile friendly built-in interface, or (optionally) redirection. This is the main payment button, 1 single button for multiple available payments methods. Please follow "how-to configure guide" here. Any feedback & request let us know here.', 'midtrans-woocommerce');
}
/**
diff --git a/class/class.midtrans-utils.php b/class/class.midtrans-utils.php
index 4cb0926..7de6a0f 100644
--- a/class/class.midtrans-utils.php
+++ b/class/class.midtrans-utils.php
@@ -130,5 +130,40 @@ function wp_get_inline_script_tag($javascript, $attributes = array()){
}
}
+ /**
+ * In case Snap API return 406 duplicate order ID, this helper func will generate
+ * new order id that is to prevent duplicate, by adding suffix on the order_id string
+ * @TAG: order-suffix-separator
+ * @param string the original WC order_id
+ * @return string the non duplicate order_id added with suffix
+ */
+ public static function generate_non_duplicate_order_id($order_id){
+ $suffix_separator = '-wc-mdtrs-';
+ $date = new DateTime();
+ $unix_timestamp = $date->getTimestamp();
+
+ $non_duplicate_order_id = $order_id.$suffix_separator.$unix_timestamp;
+ return $non_duplicate_order_id;
+ }
+
+ /**
+ * Retrieve original WC order_id from a non duplicate order_id produced by function above:
+ * generate_non_duplicate_order_id. This will check if the suffix separator exist,
+ * and split it to get the original order_id.
+ * @TAG: order-suffix-separator
+ * @param string any order_id either original or non duplicate version
+ * @return string the original WC order_id
+ */
+ public static function check_and_restore_original_order_id($non_duplicate_order_id){
+ $suffix_separator = '-wc-mdtrs-';
+ $original_order_id = $non_duplicate_order_id;
+ if(strpos($non_duplicate_order_id, $suffix_separator) !== false){
+ $splitted_order_id_strings = explode($suffix_separator, $non_duplicate_order_id);
+ // only return the left-side of the separator, ignore the rest
+ $original_order_id = $splitted_order_id_strings[0];
+ }
+ return $original_order_id;
+ }
+
}
?>
\ No newline at end of file
diff --git a/class/midtrans-admin-settings.php b/class/midtrans-admin-settings.php
index 38f6836..f12b0f8 100644
--- a/class/midtrans-admin-settings.php
+++ b/class/midtrans-admin-settings.php
@@ -64,7 +64,7 @@
'notification_url_display' => array(
'title' => __( 'Notification URL value', 'midtrans-woocommerce' ),
'type' => 'title',
- 'description' => __( 'After you have filled required config above, don\'t forget to scroll to bottom and click Save Changes button.Copy and use this recommended Notification URL '.$this->get_main_notification_url().'
into "Midtrans Dashboard > Settings > Configuration > Notification Url". This will allow your WooCommerce to receive Midtrans payment status, which auto sync the payment status.','midtrans-woocommerce'),
+ 'description' => __( 'After you have filled required config above, don\'t forget to scroll to bottom and click Save Changes button.Copy and use this recommended Notification URL '.$this->get_main_notification_url().'
into "Midtrans Dashboard > Settings > Configuration > Notification Url". This will allow your WooCommerce to receive Midtrans payment status, which auto sync the payment status.','midtrans-woocommerce'),
),
'label_config_separator' => array(
'title' => __( 'II. Payment Buttons Appereance Section - Optional', 'midtrans-woocommerce' ),
@@ -87,7 +87,7 @@
'sub_payment_method_image_file_names_str' => array(
'title' => __( 'Button Icons', 'midtrans-woocommerce' ),
'type' => 'text',
- 'description' => __( 'You can input multiple payment method names separated by coma (,). See all available values here, you can copy paste the value, and adjust as needed. Also support https:// url to external image.', 'midtrans-woocommerce' ),
+ 'description' => __( 'You can input multiple payment method names separated by coma (,). See all available values here, you can copy paste the value, and adjust as needed. Also support https:// url to external image.', 'midtrans-woocommerce' ),
'placeholder' => 'midtrans.png,credit_card.png',
),
'advanced_config_separator' => array(
@@ -164,7 +164,7 @@
'type' => 'checkbox',
'label' => 'Immediately reduce item stock on Midtrans payment pop-up?',
'description' => __( 'By default, item stock only reduced if payment status on Midtrans reach pending/success (customer choose payment channel and click pay on payment pop-up). Enable this if you want to immediately reduce item stock when payment pop-up generated/displayed.', 'midtrans-woocommerce' ),
- 'default' => 'yes'
+ 'default' => 'no'
),
// @Note: only main plugin class config will be applied on notif handler, sub plugin class config will not affect it, check gateway-notif-handler.php class to fix
'ignore_pending_status' => array(
diff --git a/midtrans-gateway.php b/midtrans-gateway.php
index 3662d71..b6d8a8f 100644
--- a/midtrans-gateway.php
+++ b/midtrans-gateway.php
@@ -3,7 +3,7 @@
Plugin Name: Midtrans - WooCommerce Payment Gateway
Plugin URI: https://github.com/veritrans/SNAP-Woocommerce
Description: Accept all payment directly on your WooCommerce site in a seamless and secure checkout environment with Midtrans
-Version: 2.30.1
+Version: 2.31.0
Author: Midtrans
Author URI: http://midtrans.co.id
License: GPLv2 or later
diff --git a/public/images/payment-methods/qris_1.png b/public/images/payment-methods/alt_qris.png
similarity index 100%
rename from public/images/payment-methods/qris_1.png
rename to public/images/payment-methods/alt_qris.png
diff --git a/public/js/midtrans-payment-page-main.js b/public/js/midtrans-payment-page-main.js
index 739d9de..cf0ad3b 100644
--- a/public/js/midtrans-payment-page-main.js
+++ b/public/js/midtrans-payment-page-main.js
@@ -3,6 +3,21 @@
;(function( $, window, document ) {
var payButton = document.getElementById("pay-button");
+ /**
+ * JS version of func `check_and_restore_original_order_id` of class `WC_Midtrans_Utils`
+ * @TAG: order-suffix-separator
+ */
+ function check_and_restore_original_order_id(non_duplicate_order_id){
+ var suffix_separator = '-wc-mdtrs-';
+ var original_order_id = non_duplicate_order_id;
+ if(non_duplicate_order_id && non_duplicate_order_id.indexOf(suffix_separator)>0){
+ var splitted_order_id_strings = non_duplicate_order_id.split(suffix_separator);
+ // only return the left-side of the separator, ignore the rest
+ original_order_id = splitted_order_id_strings[0];
+ }
+ return original_order_id;
+ }
+
function MixpanelTrackResult(token, merchant_id, cms_name, cms_version, plugin_name, plugin_version, status, result) {
var eventNames = {
pay: 'pg-pay',
@@ -71,6 +86,9 @@
if(wc_midtrans.is_using_map_finish_url){
var finish_url = result.finish_redirect_url;
} else {
+ // @TODO: `&order_id=` param may no longer needed, since we use finish_url_user_cookies
+ // @TAG: order-id-suffix-handling
+ result.order_id = check_and_restore_original_order_id(result.order_id);
var finish_url = wc_midtrans.finish_url+"&order_id="+result.order_id+"&status_code="+result.status_code+"&transaction_status="+result.transaction_status;
}
window.location = finish_url;
@@ -81,6 +99,8 @@
if (result.fraud_status == 'challenge'){ // if challenge redirect to finish
payButton.innerHTML = "Loading...";
+ // @TAG: order-id-suffix-handling
+ result.order_id = check_and_restore_original_order_id(result.order_id);
window.location = wc_midtrans.finish_url+"&order_id="+result.order_id+"&status_code="+result.status_code+"&transaction_status="+result.transaction_status;
}
@@ -88,6 +108,8 @@
// prevent redirect
var pending_url = '#';
} else {
+ // @TAG: order-id-suffix-handling
+ result.order_id = check_and_restore_original_order_id(result.order_id);
var pending_url = wc_midtrans.pending_url+"&order_id="+result.order_id+"&status_code="+result.status_code+"&transaction_status="+result.transaction_status;
// redirect to thank you page
window.location = pending_url;
@@ -106,6 +128,8 @@
MixpanelTrackResult(SNAP_TOKEN, MERCHANT_ID, CMS_NAME, CMS_VERSION, PLUGIN_NAME, PLUGIN_VERSION, 'error', result);
// console.log(result?result:'no result');
payButton.innerHTML = "Loading...";
+ // @TAG: order-id-suffix-handling
+ result.order_id = check_and_restore_original_order_id(result.order_id);
window.location = wc_midtrans.error_url+"&order_id="+result.order_id+"&status_code="+result.status_code+"&transaction_status="+result.transaction_status;
},
onClose: function(){
diff --git a/readme.txt b/readme.txt
index 942e359..ede9605 100644
--- a/readme.txt
+++ b/readme.txt
@@ -3,7 +3,7 @@ Contributors: yocki, rizdaprasetya
Tags: midtrans, snap, payment, payment-gateway, credit-card, commerce, e-commerce, woocommerce, veritrans
Requires at least: 3.9.1
Tested up to: 5.8
-Stable tag: 2.30.1
+Stable tag: 2.31.0
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -13,21 +13,21 @@ Midtrans-WooCommerce is plugin for Midtrans, Indonesian Payment Gateway. Brings
This plugin will allow secure online payment on your WooCommerce store, without your customer ever need to leave your WooCommerce store!
-Midtrans-WooCommerce is official plugin from [Midtrans](https://midtrans.com), Indonesian Payment Gateway. Brings safety and highly dedicated to customer experience (UX) to WooCommerce.Support various online payment channel.
-Support WooCommerce v3 & v2.
+Midtrans-WooCommerce is official plugin from [Midtrans](https://midtrans.com). Midtrans is an online payment gateway. We strive to make payments simple & secure for both the merchant and customers. Support various online payment channel. Support WooCommerce v3 & v2.
-Please follow [this step by step guide](https://docs.midtrans.com/en/snap/with-plugins?id=wordpress-woocommerce) for complete configuration.
+Please follow [this step by step guide](https://docs.midtrans.com/en/snap/with-plugins?id=wordpress-woocommerce) for complete configuration. If you have any feedback or request, please [do let us know here](https://docs.midtrans.com/en/snap/with-plugins?id=feedback-and-request).
Payment Method Feature:
* Credit card fullpayment and other payment methods.
-* Bank transfer, internet banking for various banks
+* E-wallet, Bank transfer, internet banking for various banks
* Credit card Online & offline installment payment.
* Credit card BIN, bank transfer, and other channel promo payment.
* Credit card MIGS acquiring channel.
* Custom expiry.
* Two-click & One-click feature.
-* Midtrans Snap all payment method fullpayment.
+* Midtrans Snap all supported payment method.
+* Optional: Separated specific payment buttons with its own icons.
== Installation ==
@@ -72,6 +72,12 @@ The best way please email to support@midtrans.com, but bugs can be reported in o
== Changelog ==
+= 2.31.0 - 2021-08-26 =
+* handle duplicated Snap order_id (incase WP is reinstalled, or DB restored) by auto-adding suffix
+* improvement on finish url redirect flow, to prevent issue
+* handle uncaught error on finish url
+* immediate-reduce-stock disabled by default
+
= 2.30.1 - 2021-08-09 =
* prevent issue "cannot inherit abstract function" on outdated PHP v5.0.0 - v5.3.8 & v7.0.0 - v7.1.x
* minor description improvement
@@ -255,6 +261,12 @@ The best way please email to support@midtrans.com, but bugs can be reported in o
== Upgrade Notice ==
+= 2.31.0 - 2021-08-26 =
+* handle duplicated Snap order_id (incase WP is reinstalled, or DB restored) by auto-adding suffix
+* improvement on finish url redirect flow, to prevent issue
+* handle uncaught error on finish url
+* immediate-reduce-stock disabled by default
+
= 2.30.1 - 2021-08-09 =
* prevent issue "cannot inherit abstract function" on outdated PHP v5.0.0 - v5.3.8 & v7.0.0 - v7.1.x
* minor description improvement
@@ -409,6 +421,6 @@ Support additional feature like installment, MIGS acq, and bin promo.
== Get Help ==
* [Midtrans WooCommerce Configuration Guide](https://docs.midtrans.com/en/snap/with-plugins?id=wordpress-woocommerce)
* [Midtrans registration](https://account.midtrans.com/register)
+* [Midtrans Support Contact](https://midtrans.com/id/contact-us)
* [Midtrans Documentation](https://docs.midtrans.com)
-* [Midtrans Snap API Documentation](https://snap-docs.midtrans.com)
* [Midtrans-WooCommerce Wiki](https://github.com/veritrans/SNAP-Woocommerce/wiki)
\ No newline at end of file