├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── composer.json
├── phpunit.xml.dist
└── src
└── Omnipay
└── WeChat
├── ExpressGateway.php
└── Message
├── BaseAbstractRequest.php
├── WechatCompletePurchaseRequest.php
├── WechatCompletePurchaseResponse.php
├── WechatPrePurchaseRequest.php
├── WechatPurchaseRequest.php
└── WechatPurchaseResponse.php
/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /vendor
3 | composer.lock
4 | phpunit.xml
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.4
5 | - 5.5
6 | - 5.6
7 | - hhvm
8 |
9 | before_script:
10 | - composer self-update
11 | - composer install -n --dev --prefer-source
12 |
13 | script: vendor/bin/phpcs --standard=PSR2 src && vendor/bin/phpunit --coverage-clover=coverage.xml
14 |
15 | after_success:
16 | - bash <(curl -s https://codecov.io/bash)
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Omnipay-WeChat
2 | ===
3 |
4 | **WeChat Payment driver for the Omnipay PHP payment processing library**
5 |
6 | [](https://github.com/labs7in0/omnipay-wechat)
7 | [](https://travis-ci.org/labs7in0/omnipay-wechat)
8 | [](https://codecov.io/github/labs7in0/omnipay-wechat)
9 | [](https://packagist.org/packages/labs7in0/omnipay-wechat)
10 | [](https://packagist.org/packages/labs7in0/omnipay-wechat)
11 |
12 | **Deprecated** We suggest you to use [@lokielse](https://github.com/lokielse)'s implementation of WeChatPay for Omnipay at [lokielse/omnipay-wechatpay](https://github.com/lokielse/omnipay-wechatpay).
13 |
14 | There's a pre-built Payment Gateway based on Omnipay at [labs7in0/E-cash](https://github.com/labs7in0/E-cash).
15 |
16 | ## Installation
17 |
18 | Omnipay is installed via [Composer](http://getcomposer.org/). To install, simply add it
19 | to your `composer.json` file:
20 |
21 | ```json
22 | {
23 | "require": {
24 | "labs7in0/omnipay-wechat": "dev-master"
25 | }
26 | }
27 | ```
28 |
29 | And run composer to update your dependencies:
30 |
31 | $ curl -s http://getcomposer.org/installer | php
32 | $ php composer.phar update
33 |
34 | ## Basic Usage
35 |
36 | The following gateways are provided by this package:
37 |
38 | * WeChat Express (WeChat NATIVE)
39 |
40 | For general usage instructions, please see the main [Omnipay](https://github.com/thephpleague/omnipay)
41 | repository.
42 |
43 | ## Example
44 |
45 | ### Make a payment
46 |
47 | The WeChat NATIVE payment gateway return a URI which can be opened within WeChat In-App broswer, you can generate a QR code with the URI.
48 |
49 | ```php
50 | $omnipay = Omnipay::create('WeChat_Express');
51 |
52 | $omnipay->setAppId('app_id'); // App ID of your WeChat MP account
53 | $omnipay->setAppKey('app_key'); // App Key of your WeChat MP account
54 | $omnipay->setMchId('partner_id'); // Partner ID of your WeChat merchandiser (WeChat Pay) account
55 |
56 | $params = array(
57 | 'out_trade_no' => time() . rand(100, 999), // billing id in your system
58 | 'notify_url' => $notify_url, // URL for asynchronous notify
59 | 'body' => $billing_desc, // A simple description
60 | 'total_fee' => 0.01, // Amount with less than 2 decimals places
61 | 'fee_type' => 'CNY', // Currency name from ISO4217, Optional, default as CNY
62 | );
63 |
64 | $response = $omnipay->purchase($params)->send();
65 |
66 | $qrCode = new Endroid\QrCode\QrCode(); // Use Endroid\QrCode to generate the QR code
67 | $qrCode
68 | ->setText($response->getRedirectUrl())
69 | ->setSize(120)
70 | ->setPadding(0)
71 | ->render();
72 | ```
73 |
74 | ### Verify a payment (especially for asynchronous notify)
75 |
76 | `completePurchase` for Omnipay-WeChat does not require the same arguments as when you made the initial `purchase` call. The only required parameter is `out_trade_no` (the billing id in your system) or `transaction_id` (the trade number from WeChat).
77 |
78 | ```php
79 | $omnipay = Omnipay::create('WeChat_Express');
80 |
81 | $omnipay->setAppId('app_id'); // App ID of your WeChat MP account
82 | $omnipay->setAppKey('app_key'); // App Key of your WeChat MP account
83 | $omnipay->setMchId('partner_id'); // Partner ID of your WeChat merchandiser (WeChat Pay) account
84 |
85 | $params = array(
86 | 'out_trade_no' => $billing_id, // billing id in your system
87 | //or you can use 'transaction_id', the trade number from WeChat
88 | );
89 |
90 | $response = $omnipay->completePurchase($params)->send();
91 |
92 | if ($response->isSuccessful() && $response->isTradeStatusOk()) {
93 | $responseData = $response->getData();
94 |
95 | // Do something here
96 | }
97 |
98 | ```
99 |
100 | ## Donate us
101 |
102 | [Donate us](https://7in0.me/#donate)
103 |
104 | ## License
105 | The MIT License (MIT)
106 |
107 | More info see [LICENSE](LICENSE)
108 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "labs7in0/omnipay-wechat",
3 | "description": "WeChat driver for the Omnipay PHP payment processing library",
4 | "keywords": [
5 | "gateway",
6 | "merchant",
7 | "omnipay",
8 | "pay",
9 | "payment",
10 | "purchase",
11 | "wechat"
12 | ],
13 | "homepage": "https://github.com/labs7in0/omnipay-wechat",
14 | "type": "library",
15 | "autoload": {
16 | "psr-0": {"Omnipay\\WeChat\\": "src/"}
17 | },
18 | "require": {
19 | "omnipay/common": "~2.0"
20 | },
21 | "require-dev": {
22 | "omnipay/tests": "~2.0"
23 | },
24 | "license": "MIT",
25 | "authors": [
26 | {
27 | "name": "7IN0SAN9",
28 | "email": "me@7in0.me"
29 | }
30 | ],
31 | "minimum-stability": "beta"
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 | ./tests/
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | ./src
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/Omnipay/WeChat/ExpressGateway.php:
--------------------------------------------------------------------------------
1 | setParameter('app_id', $appId);
17 | }
18 |
19 | public function getAppId()
20 | {
21 | return $this->getParameter('app_id');
22 | }
23 |
24 | public function setAppKey($appKey)
25 | {
26 | $this->setParameter('app_key', $appKey);
27 | }
28 |
29 | public function getAppKey()
30 | {
31 | return $this->getParameter('app_key');
32 | }
33 |
34 | public function setMchId($mchId)
35 | {
36 | $this->setParameter('mch_id', $mchId);
37 | }
38 |
39 | public function getMchId()
40 | {
41 | return $this->getParameter('mch_id');
42 | }
43 |
44 | public function setNotifyUrl($url)
45 | {
46 | $this->setParameter('notify_url', $url);
47 | }
48 |
49 | public function getNotifyUrl()
50 | {
51 | return $this->getParameter('notify_url');
52 | }
53 |
54 | public function purchase($parameters = array())
55 | {
56 | if (empty($parameters['code_url'])) {
57 | $res = $this->prePurchase($parameters)->send();
58 | }
59 |
60 | return $this->createRequest('\Omnipay\WeChat\Message\WechatPurchaseRequest', $res);
61 | }
62 |
63 | public function prePurchase($parameters = array())
64 | {
65 | $params = array(
66 | 'app_id' => $this->getAppId(),
67 | 'mch_id' => $this->getMchId(),
68 | 'device_info' => 'WEB',
69 | 'noncestr' => bin2hex(openssl_random_pseudo_bytes(8)),
70 | 'body' => $parameters['body'],
71 | 'out_trade_no' => $parameters['out_trade_no'],
72 | 'total_fee' => round($parameters['total_fee'] * 100),
73 | 'fee_type' => $parameters['fee_type'],
74 | 'spbill_create_ip' => $_SERVER['REMOTE_ADDR'],
75 | 'notify_url' => $parameters['notify_url'],
76 | 'trade_type' => 'NATIVE',
77 | );
78 |
79 | return $this->createRequest('\Omnipay\WeChat\Message\WechatPrePurchaseRequest', $params);
80 | }
81 |
82 | public function completePurchase($parameters = array())
83 | {
84 | return $this->createRequest('\Omnipay\WeChat\Message\WechatCompletePurchaseRequest', $parameters);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/Omnipay/WeChat/Message/BaseAbstractRequest.php:
--------------------------------------------------------------------------------
1 | setParameter('app_id', $appId);
12 | }
13 |
14 | public function getAppId()
15 | {
16 | return $this->getParameter('app_id');
17 | }
18 |
19 | public function setAppKey($appKey)
20 | {
21 | $this->setParameter('app_key', $appKey);
22 | }
23 |
24 | public function getAppKey()
25 | {
26 | return $this->getParameter('app_key');
27 | }
28 |
29 | public function setMchId($mchId)
30 | {
31 | $this->setParameter('mch_id', $mchId);
32 | }
33 |
34 | public function getMchId()
35 | {
36 | return $this->getParameter('mch_id');
37 | }
38 |
39 | protected function postStr($url, $data)
40 | {
41 | $ch = curl_init();
42 |
43 | $options = array(
44 | CURLOPT_HEADER => false,
45 | CURLOPT_POST => true,
46 | CURLOPT_POSTFIELDS => $data,
47 | CURLOPT_RETURNTRANSFER => true,
48 | CURLOPT_SSL_VERIFYHOST => false,
49 | CURLOPT_SSL_VERIFYPEER => false,
50 | CURLOPT_TIMEOUT => 30,
51 | CURLOPT_URL => $url,
52 | );
53 | curl_setopt_array($ch, $options);
54 | $result = curl_exec($ch);
55 | curl_close($ch);
56 |
57 | return $result;
58 | }
59 |
60 | protected function arrayToXml($arr)
61 | {
62 | $xml = "";
63 | foreach ($arr as $key => $val) {
64 | if (is_numeric($val)) {
65 | $xml .= "<" . $key . ">" . $val . "" . $key . ">";
66 |
67 | } else {
68 | $xml .= "<" . $key . ">" . $key . ">";
69 | }
70 |
71 | }
72 | $xml .= "";
73 | return $xml;
74 | }
75 |
76 | protected function xmlToArray($xml)
77 | {
78 | $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
79 | return $array_data;
80 | }
81 |
82 | protected static function httpBuildQueryWithoutNull($params)
83 | {
84 | foreach ($params as $key => $value) {
85 | if (null == $value || 'null' == $value || 'sign' == $key) {
86 | unset($params[$key]);
87 | }
88 | }
89 | reset($params);
90 | ksort($params);
91 |
92 | return http_build_query($params);
93 | }
94 |
95 | protected static function httpBuildQuery($params)
96 | {
97 | ksort($params);
98 |
99 | $str = http_build_query($params);
100 |
101 | return $str;
102 | }
103 |
104 | protected function genSign($params)
105 | {
106 | $bizParameters = array();
107 | foreach ($params as $k => $v) {
108 | $bizParameters[strtolower($k)] = $v;
109 | }
110 | $bizString = self::httpBuildQueryWithoutNull($bizParameters);
111 | $bizString .= '&key=' . $this->getAppKey();
112 |
113 | return strtoupper(md5(urldecode($bizString)));
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/Omnipay/WeChat/Message/WechatCompletePurchaseRequest.php:
--------------------------------------------------------------------------------
1 | getParameter('transaction_id');
12 | }
13 |
14 | public function setTransactionId($value)
15 | {
16 | $this->setParameter('transaction_id', $value);
17 | }
18 |
19 | public function getOutTradeNo()
20 | {
21 | $this->getParameter('out_trade_no');
22 | }
23 |
24 | public function setOutTradeNo($value)
25 | {
26 | $this->setParameter('out_trade_no', $value);
27 | }
28 |
29 | public function getData()
30 | {
31 | $this->validate(
32 | 'app_id',
33 | 'mch_id'
34 | );
35 |
36 | $params['appid'] = $this->parameters->get('app_id');
37 | $params['mch_id'] = $this->parameters->get('mch_id');
38 | $params['nonce_str'] = bin2hex(openssl_random_pseudo_bytes(8));
39 | $params['transaction_id'] = $this->parameters->get('transaction_id');
40 | $params['out_trade_no'] = $this->parameters->get('out_trade_no');
41 | return $params;
42 | }
43 |
44 | public function sendData($data)
45 | {
46 | $data = array(
47 | 'appid' => $data['appid'],
48 | 'mch_id' => $data['mch_id'],
49 | 'nonce_str' => $data['nonce_str'],
50 | 'transaction_id' => $data['transaction_id'],
51 | 'out_trade_no' => $data['out_trade_no'],
52 | );
53 |
54 | $data['sign'] = $this->genSign($data);
55 |
56 | $data = $this->arrayToXml($data);
57 |
58 | $data = $this->xmlToArray($this->postStr($this->endpoint, $data));
59 |
60 | return new WechatCompletePurchaseResponse($this, $data);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Omnipay/WeChat/Message/WechatCompletePurchaseResponse.php:
--------------------------------------------------------------------------------
1 | getData();
12 | if ($result['return_code'] == 'SUCCESS' && $result['result_code'] == 'SUCCESS') {
13 | return true;
14 | } else {
15 | return false;
16 | }
17 | }
18 |
19 | public function isRedirect()
20 | {
21 | return false;
22 | }
23 |
24 | public function isTradeStatusOk()
25 | {
26 | $result = $this->getData();
27 | return $result['trade_state'] == 'SUCCESS';
28 | }
29 |
30 | public function getMessage()
31 | {
32 | $result = $this->getData();
33 | return $result['return_msg'];
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Omnipay/WeChat/Message/WechatPrePurchaseRequest.php:
--------------------------------------------------------------------------------
1 | response) {
14 | throw new \RuntimeException('Request cannot be modified after it has been sent!');
15 | }
16 |
17 | $this->parameters = new ParameterBag;
18 | foreach ($parameters as $k => $v) {
19 | $this->parameters->set($k, $v);
20 | }
21 | return $this;
22 | }
23 |
24 | public function getData()
25 | {
26 | $this->validate(
27 | 'app_id',
28 | 'mch_id',
29 | 'body',
30 | 'out_trade_no',
31 | 'total_fee',
32 | 'spbill_create_ip',
33 | 'notify_url',
34 | 'trade_type'
35 | );
36 |
37 | $params['appid'] = $this->parameters->get('app_id');
38 | $params['mch_id'] = $this->parameters->get('mch_id');
39 | $params['nonce_str'] = bin2hex(openssl_random_pseudo_bytes(8));
40 | $params['body'] = $this->parameters->get('body');
41 | $params['out_trade_no'] = $this->parameters->get('out_trade_no');
42 | $params['total_fee'] = $this->parameters->get('total_fee');
43 | $params['spbill_create_ip'] = $this->parameters->get('spbill_create_ip');
44 | $params['time_start'] = date('YmdHis');
45 | $params['notify_url'] = $this->parameters->get('notify_url');
46 | $params['trade_type'] = $this->parameters->get('trade_type');
47 | $params['product_id'] = $this->parameters->get('out_trade_no');
48 | return $params;
49 | }
50 |
51 | public function sendData($data)
52 | {
53 | $data = array(
54 | 'appid' => $data['appid'],
55 | 'mch_id' => $data['mch_id'],
56 | 'device_info' => 'WEB',
57 | 'nonce_str' => $data['nonce_str'],
58 | 'body' => $data['body'],
59 | 'out_trade_no' => $data['out_trade_no'],
60 | 'total_fee' => $data['total_fee'],
61 | 'spbill_create_ip' => $data['spbill_create_ip'],
62 | 'time_start' => $data['time_start'],
63 | 'notify_url' => $data['notify_url'],
64 | 'trade_type' => $data['trade_type'],
65 | 'product_id' => $data['product_id'],
66 | );
67 |
68 | $data['sign'] = $this->genSign($data);
69 |
70 | $data = $this->arrayToXml($data);
71 |
72 | return $this->xmlToArray($this->postStr($this->endpoint, $data));
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/Omnipay/WeChat/Message/WechatPurchaseRequest.php:
--------------------------------------------------------------------------------
1 | response) {
12 | throw new \RuntimeException('Request cannot be modified after it has been sent!');
13 | }
14 |
15 | $this->parameters = new ParameterBag;
16 | foreach ($parameters as $k => $v) {
17 | $this->parameters->set($k, $v);
18 | }
19 | return $this;
20 | }
21 |
22 | public function getData()
23 | {
24 | $this->validate('code_url', 'timestamp');
25 |
26 | $params['code_url'] = $this->parameters->get('code_url');
27 | $params['timestamp'] = $this->parameters->get('timestamp');
28 |
29 | if (empty($params['code_url'])) {
30 | throw new \RuntimeException('The code_url that pre-purchase responded is empty, check your parameters!');
31 | }
32 |
33 | return $params;
34 | }
35 |
36 | public function sendData($data)
37 | {
38 | return new WechatPurchaseResponse($this, $data);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Omnipay/WeChat/Message/WechatPurchaseResponse.php:
--------------------------------------------------------------------------------
1 | getData();
11 | return !empty($data['code_url']);
12 | }
13 |
14 | public function isSuccessful()
15 | {
16 | return false;
17 | }
18 |
19 | public function getRedirectUrl()
20 | {
21 | $data = $this->getData();
22 | return $data['code_url'];
23 | }
24 |
25 | public function getRedirectMethod()
26 | {
27 | return 'GET';
28 | }
29 |
30 | public function getRedirectData()
31 | {
32 | return null;
33 | }
34 |
35 | public function redirect()
36 | {
37 | return $this->getRedirectUrl();
38 | }
39 | }
40 |
--------------------------------------------------------------------------------