Skip to content

Commit

Permalink
Add wechat pay (#434)
Browse files Browse the repository at this point in the history
* Implemented WeChat Pay.

* Added tests

* Added tests for get_client_ip

* Add missing require of wechat in omise-woocommerce.

---------

Co-authored-by: Aashish <aashish@omise.co>
  • Loading branch information
aashishgurung and Aashish authored Mar 4, 2024
1 parent 326625d commit c83fe8e
Show file tree
Hide file tree
Showing 10 changed files with 243 additions and 9 deletions.
3 changes: 3 additions & 0 deletions assets/images/wechat_pay.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 1 addition & 2 deletions includes/class-omise-callback.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,14 @@ public static function execute()
$order_id = isset( $_GET['order_id'] ) ? sanitize_text_field( $_GET['order_id'] ) : null;
$order = wc_get_order( $order_id );

if(!RequestHelper::validateRequest($order->get_meta('token'))) {
if(!RequestHelper::validate_request($order->get_meta('token'))) {
return wp_redirect( wc_get_checkout_url() );
}

$callback = new self( $order );
$callback->validate();
}


/**
* Sometimes cancelling a transaction does not updates the status on the Omise backend
* which causes the status to be pending even thought the transaction was cancelled.
Expand Down
3 changes: 2 additions & 1 deletion includes/class-omise-payment-factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ class Omise_Payment_Factory {
'Omise_Payment_DuitNow_QR',
'Omise_Payment_DuitNow_OBW',
'Omise_Payment_Atome',
'Omise_Payment_PayPay'
'Omise_Payment_PayPay',
'Omise_Payment_Wechat_Pay',
);

/**
Expand Down
83 changes: 83 additions & 0 deletions includes/gateway/class-omise-payment-wechat-pay.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php
defined( 'ABSPATH' ) or die( 'No direct script access allowed.' );

class Omise_Payment_Wechat_Pay extends Omise_Payment_Offsite
{
public function __construct()
{
parent::__construct();

$this->id = 'omise_wechat_pay';
$this->has_fields = true;
$this->method_title = __( 'Opn Payments WeChat Pay', 'omise' );
$this->method_description = wp_kses(
__( 'Accept payment through <strong>WeChat Pay</strong> via Opn Payments payment gateway.', 'omise' ),
['strong' => []]
);

$this->init_form_fields();
$this->init_settings();

$this->title = $this->get_option( 'title' );
$this->description = $this->get_option( 'description' );
$this->restricted_countries = [ 'TH' ];
$this->source_type = 'wechat_pay';

add_action( 'woocommerce_api_' . $this->id . '_callback', 'Omise_Callback::execute' );
add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
add_action( 'woocommerce_order_action_' . $this->id . '_sync_payment', array( $this, 'sync_payment' ) );
}

/**
* @see WC_Settings_API::init_form_fields()
* @see woocommerce/includes/abstracts/abstract-wc-settings-api.php
*/
public function init_form_fields()
{
$this->form_fields = array(
'enabled' => array(
'title' => __('Enable/Disable', 'omise'),
'type' => 'checkbox',
'label' => __('Enable Opn Payments WeChat Pay', 'omise'),
'default' => 'no'
),

'title' => array(
'title' => __('Title', 'omise'),
'type' => 'text',
'description' => __('This controls the title the user sees during checkout.', 'omise'),
'default' => __('WeChat Pay', 'omise'),
),

'description' => array(
'title' => __('Description', 'omise'),
'type' => 'textarea',
'description' => __('This controls the description the user sees during checkout.', 'omise')
),
);
}

/**
* Get icons
*
* @see WC_Payment_Gateway::get_icon()
*/
public function get_icon()
{
$icon = Omise_Image::get_image([
'file' => 'wechat_pay.svg',
'alternate_text' => 'WeChat Pay',
]);

return apply_filters('woocommerce_gateway_icon', $icon, $this->id);
}

public function charge($order_id, $order)
{
$requestData = $this->build_charge_request(
$order_id, $order, $this->source_type, $this->id . "_callback"
);
$requestData['source']['ip'] = RequestHelper::get_client_ip();
return OmiseCharge::create($requestData);
}
}
51 changes: 46 additions & 5 deletions includes/libraries/omise-plugin/helpers/request.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?php
if (! class_exists('RequestHelper')) {
if (!class_exists('RequestHelper')) {
class RequestHelper
{
/**
Expand All @@ -9,7 +9,7 @@ class RequestHelper
*
* Ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Site
*/
public static function isUserOriginated()
public static function is_user_originated()
{
$fetch_site = sanitize_text_field($_SERVER['HTTP_SEC_FETCH_SITE']);

Expand All @@ -20,17 +20,58 @@ public static function isUserOriginated()
/**
* @param string|null $order_token
*/
public static function validateRequest($order_token = null)
public static function validate_request($order_token = null)
{
$token = isset( $_GET['token'] ) ? sanitize_text_field( $_GET['token'] ) : null;
$token = isset($_GET['token']) ? sanitize_text_field($_GET['token']) : null;

// For all payment except offline and OCBC PAO.
if ($token) {
return $token === $order_token;
}

// For offline payment methods does not include token in the return URI.
return !self::isUserOriginated();
return !self::is_user_originated();
}

public static function get_client_ip()
{
$headersToCheck = [
// Check for a client using a shared internet connection
'HTTP_CLIENT_IP',

// Check if the proxy is used for IP/IPs
'HTTP_X_FORWARDED_FOR',

// check for other possible forwarded IP headers
'HTTP_X_FORWARDED',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED',
];

foreach($headersToCheck as $header) {
if (empty($_SERVER[$header])) {
continue;
}

if ($header === 'HTTP_X_FORWARDED_FOR') {
return self::process_forwarded_for_header($_SERVER[$header]);
}

return $_SERVER[$header];
}

// return default remote IP address
return $_SERVER['REMOTE_ADDR'];
}

private static function process_forwarded_for_header($forwardedForHeader)
{
// Split if multiple IP addresses exist and get the last IP address
if (strpos($forwardedForHeader, ',') !== false) {
$multiple_ips = explode(",", $forwardedForHeader);
return trim(current($multiple_ips));
}
return $forwardedForHeader;
}
}
}
1 change: 1 addition & 0 deletions omise-woocommerce.php
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ private function include_classes()
require_once OMISE_WOOCOMMERCE_PLUGIN_PATH . '/includes/gateway/class-omise-payment.php';
require_once OMISE_WOOCOMMERCE_PLUGIN_PATH . '/includes/gateway/class-omise-payment-atome.php';
require_once OMISE_WOOCOMMERCE_PLUGIN_PATH . '/includes/gateway/class-omise-payment-paypay.php';
require_once OMISE_WOOCOMMERCE_PLUGIN_PATH . '/includes/gateway/class-omise-payment-wechat-pay.php';
require_once OMISE_WOOCOMMERCE_PLUGIN_PATH . '/includes/libraries/omise-php/lib/Omise.php';
require_once OMISE_WOOCOMMERCE_PLUGIN_PATH . '/includes/libraries/omise-plugin/Omise.php';
require_once OMISE_WOOCOMMERCE_PLUGIN_PATH . '/includes/class-omise-ajax-actions.php';
Expand Down
8 changes: 8 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,12 @@
<directory suffix=".php">vendor</directory>
</exclude>
</coverage>
<php>
<server name="HTTP_CLIENT_IP" value="192.168.1.1"/>
<server name="HTTP_X_FORWARDED_FOR" value="192.168.1.2"/>
<server name="HTTP_X_FORWARDED" value="192.168.1.3"/>
<server name="HTTP_FORWARDED_FOR" value="192.168.1.4"/>
<server name="HTTP_FORWARDED" value="192.168.1.5"/>
<server name="REMOTE_ADDR" value="192.168.1.6"/>
</php>
</phpunit>
1 change: 0 additions & 1 deletion tests/unit/includes/gateway/bootstrap-test-setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

use PHPUnit\Framework\TestCase;


abstract class Bootstrap_Test_Setup extends TestCase
{
public $sourceType;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

require_once __DIR__ . '/class-omise-offsite-test.php';

class Omise_Payment_Wechat_Pay_Test extends Omise_Offsite_Test
{
public function setUp(): void
{
parent::setUp();
$this->sourceType = 'wechat_pay';

Brain\Monkey\setUp();
Brain\Monkey\Functions\stubs([
'apply_filters' => function () {
return Omise_Image::get_image([
'file' => 'wechat_pay.svg',
'alternate_text' => 'WeChat Pay',
]);
},
]);

require_once __DIR__ . '/../../../../includes/libraries/omise-plugin/helpers/request.php';
require_once __DIR__ . '/../../../../includes/gateway/class-omise-payment-wechat-pay.php';
}

public function test_restricted_countries_field_has_required_countries()
{
$obj = new Omise_Payment_Wechat_Pay();
$expectedCountries = ['TH'];

$this->assertEqualsCanonicalizing($expectedCountries, $obj->restricted_countries);
unset($expectedCountries);
}

public function test_charge()
{
$obj = new Omise_Payment_Wechat_Pay();
$this->getChargeTest($obj);
}

public function test_get_icon()
{
$obj = new Omise_Payment_Wechat_Pay();
$res = $obj->get_icon();
$expected = "<img src='/wechat_pay.svg' class='Omise-Image' style='width: 30px; max-height: 30px;' alt='WeChat Pay' />";
$this->assertEquals($expected, trim($res));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

use PHPUnit\Framework\TestCase;

class RequestHelperTest extends TestCase
{
public function setUp(): void
{
require_once __DIR__ . '/../../../../../../includes/libraries/omise-plugin/helpers/request.php';
}

/**
* @dataProvider get_client_ip_data_provider
* @covers RequestHelper
*/
public function test_get_client_ip($serverArrKeyToTest)
{
$ipHeaderKeys = [
'HTTP_CLIENT_IP',
'HTTP_X_FORWARDED_FOR',
'HTTP_X_FORWARDED',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED',
'REMOTE_ADDR',
];

foreach($ipHeaderKeys as $ipHeaderKey) {
if ($ipHeaderKey !== $serverArrKeyToTest) {
$_SERVER[$ipHeaderKey] = null;
}
}

$res = RequestHelper::get_client_ip();
$this->assertEquals($_SERVER[$serverArrKeyToTest], $res);
}

/**
* Data provider for toSubunitReturnCorrectFormat
*/
public function get_client_ip_data_provider()
{
return [
['HTTP_CLIENT_IP'],
['HTTP_X_FORWARDED_FOR'],
['HTTP_X_FORWARDED'],
['HTTP_FORWARDED_FOR'],
['HTTP_FORWARDED'],
['REMOTE_ADDR'],
];
}
}

0 comments on commit c83fe8e

Please sign in to comment.