cmb_pay 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 973b9cf0d5d2974860b0d7fff0cd75d570adc8d6
4
+ data.tar.gz: 192ec7d563ace627bcd1e832d5691a8f76268ecf
5
+ SHA512:
6
+ metadata.gz: 3cd712678f799430ce5c34b9382405448f837fe0752c28bfff61aea7b4f8af51043574c946c8f8231d024f9acf157c8753b50943e1a043932de9d2c70c1ea278
7
+ data.tar.gz: 50472f3eabe787fe14e9a107404775d31b26108fa4a40fd56b77b80681e5ea4867480595a6312e733c804eb53e2629009ec73d7d7d7d39bc3616c5dc0f5def04
checksums.yaml.gz.sig ADDED
Binary file
data.tar.gz.sig ADDED
@@ -0,0 +1,5 @@
1
+ ��7ͷ8��gC $�h��!�y�R6�0[H�Zե1SL�"�p��&�۳�� �O�a�qF��b�Žl�?��
2
+ _�~t��S��yeb5O#'�
3
+ 3e��yqj6���߯��T��a�|���U��W�\�� �V��΄�L���_|O*��<��i�ۼ��4���~�NאJ �C�k���昼oK
4
+ �H�)eH_�;����#���C�4�^�6Rq{.>�����1o.Q��3�YW��f�{�O�c(�����
5
+ !o3
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ # Ignore rubymine files
2
+ /.idea
3
+ .DS_Store
4
+ /.bundle/
5
+ /.yardoc
6
+ /Gemfile.lock
7
+ /_yardoc/
8
+ /coverage/
9
+ /doc/
10
+ /pkg/
11
+ /spec/reports/
12
+ /tmp/
13
+ *.sublime-workspace
14
+ spec/examples.txt
15
+ .byebug_history
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,23 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.1
3
+
4
+ Documentation:
5
+ Enabled: false
6
+
7
+ Metrics/LineLength:
8
+ Max: 150
9
+
10
+ Metrics/AbcSize:
11
+ Max: 57
12
+
13
+ Metrics/ClassLength:
14
+ Max: 150
15
+
16
+ Metrics/MethodLength:
17
+ Max: 20
18
+
19
+ Style/NumericLiterals:
20
+ MinDigits: 7
21
+
22
+ Style/AsciiComments:
23
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.2.5
5
+ - 2.3.1
6
+ before_install: gem install bundler -v 1.12.5
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 guochunzhong
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ CMB Pay [![Gem Version][version-badge]][rubygems] [![Build Status][travis-badge]][travis] [![Code Climate][codeclimate-badge]][codeclimate] [![Code Coverage][codecoverage-badge]][codecoverage]
2
+ =======
3
+
4
+ An unofficial cmb (China Merchants Bank) pay ruby gem, inspired from [alipay](https://github.com/chloerei/alipay), [wx_pay](https://github.com/jasl/wx_pay) and [cmbchina](https://github.com/yellong/cmbchina) a lot.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'cmb_pay'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install cmb_pay
21
+
22
+ ## Usage
23
+
24
+ ### Config
25
+
26
+ Create `config/initializers/cmb_pay.rb` and put following configurations into it.
27
+
28
+ ```ruby
29
+ # required
30
+ CmbPay.branch_id = '0755' # 支付商户开户分行号,4位
31
+ CmbPay.co_no = '000056' # 支付商户号/收单商户号,6位长数字,由银行在商户开户时确定
32
+ CmbPay.environment = 'test' if Rails.env.development? || Rails.env.staging?
33
+ CmbPay.co_key = '' # 商户校验码,测试环境为空
34
+ # only require by uri_of_pre_pay_euserp
35
+ CmbPay.mch_no = 'P0019844' # 协议商户企业编号,或者说是8位虚拟企业网银编号
36
+ CmbPay.default_payee_id = '1' # 默认收款方的用户标识
37
+ ```
38
+
39
+ ## Development
40
+
41
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
42
+
43
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
44
+
45
+ ## Contributing
46
+
47
+ Bug reports and pull requests are welcome on GitHub at https://github.com/bayetech/cmb_pay. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
48
+
49
+
50
+ ## License
51
+
52
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
53
+
54
+ [version-badge]: https://badge.fury.io/rb/cmb_pay.svg
55
+ [rubygems]: https://rubygems.org/gems/cmb_pay
56
+ [travis-badge]: https://travis-ci.org/bayetech/cmb_pay.svg
57
+ [travis]: https://travis-ci.org/bayetech/cmb_pay
58
+ [codeclimate-badge]: https://codeclimate.com/github/bayetech/cmb_pay/badges/gpa.svg
59
+ [codeclimate]: https://codeclimate.com/github/bayetech/cmb_pay
60
+ [codecoverage-badge]: https://codeclimate.com/github/bayetech/cmb_pay/badges/coverage.svg
61
+ [codecoverage]: https://codeclimate.com/github/bayetech/cmb_pay/coverage
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'cmb_pay'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,21 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIDeDCCAmCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBBMRMwEQYDVQQDDAplcmlj
3
+ Lmd1b2N6MRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZFgNj
4
+ b20wHhcNMTYwNzExMTUyMTU2WhcNMTcwNzExMTUyMTU2WjBBMRMwEQYDVQQDDApl
5
+ cmljLmd1b2N6MRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZ
6
+ FgNjb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6LSiHb79c6Krm
7
+ TeoHq5fko/QJownbKLtlzHmWCGb6B38mIPqYZBSmfaK9EZr6DTf7TJWB7e/u2+Ep
8
+ bXCqdUxRDbQZig52DvKfljfgOD/YzALHKAckuk/sskvM2pdtRIowJSYAG/Hz2j1d
9
+ mngRZaIDd/09CNttRsgCDXZl+qqsNJKCQWZ3T8OdqmlpwkIxo7llKEQ578fQwgZ3
10
+ 8uE4RLMv8fOdFv0EzGXbRyFLCT2WEAH5Ns2Jv12KKbvYCTICdJ36cSV8hOUZT0fG
11
+ y4FxEXtV9uJVJv3BJ5LE84TWdNiMI1lbFul72p09vebUe2N0Hru3XDnfq69XJ+9e
12
+ nZC9MFk/AgMBAAGjezB5MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
13
+ BBR/fjIribDcJvWvxPh+zv2kFGRAejAfBgNVHREEGDAWgRRlcmljLmd1b2N6QGdt
14
+ YWlsLmNvbTAfBgNVHRIEGDAWgRRlcmljLmd1b2N6QGdtYWlsLmNvbTANBgkqhkiG
15
+ 9w0BAQUFAAOCAQEAIA++aoEJOSbTwQml1cQ+z2psV2R18HKYR7ZM8bGEm9PxGyLt
16
+ DcpqGIL65VctVjqVzD4RTvDrsFhVICL5o0rLYpC4Qm5jBpx+E2s/akJSDsQcOO12
17
+ qk+IDlQHJjhf7DOg0+XAIj1/QTvq8s3QrrD8NRoIvGcAXVzqAafcG9NOGIXpqFS1
18
+ ZdwgixrYZK4p/Z+qIJFiuGvBauegUn0x7WDln52cWXgb+a/oYPBjGtBAZznhSy+R
19
+ R5k6Ma92sW8jupX4cqbSu9rntdVQkNRpoHIrfU0MZT0cKsg/D1zMteylxrO3KMsz
20
+ SPQRv+nrI1J0zevFqb8010heoR8SDyUA0Mm3+Q==
21
+ -----END CERTIFICATE-----
@@ -0,0 +1 @@
1
+ 49dec10bce7bd6a94b0b80e6539e209a2297bcc6a6fc23699d78d5f2cdd222b36a7ce4aa6f18cfd27afed3d326891938d7279c5e41fecad986f9ea5f6af49443
data/lib/cmb_pay.rb ADDED
@@ -0,0 +1,143 @@
1
+ require 'date'
2
+ require 'uri'
3
+ require 'active_support/core_ext/hash'
4
+ require 'cmb_pay/version'
5
+ require 'cmb_pay/util'
6
+ require 'cmb_pay/sign'
7
+ require 'cmb_pay/merchant_code'
8
+ require 'cmb_pay/message'
9
+ require 'cmb_pay/service'
10
+
11
+ module CmbPay
12
+ class << self
13
+ attr_accessor :branch_id # 开户分行号
14
+ attr_accessor :co_no # 支付商户号/收单商户号
15
+ attr_accessor :co_key # 商户校验码,测试环境为空
16
+ attr_accessor :mch_no # 协议商户企业编号,或者说是8位虚拟企业网银编号
17
+ attr_accessor :expire_in_minutes # 会话有效时间
18
+ attr_accessor :environment # 调用的招商银行支付环境,默认生产,测试填test
19
+ attr_accessor :default_payee_id # 默认收款方的用户标识
20
+ end
21
+ @co_key = ''
22
+ @mch_no = ''
23
+ @expire_in_minutes = 30
24
+ @environment = :production
25
+
26
+ SUPPORTED_BANK = {
27
+ '招商银行' => nil,
28
+ '兴业银行' => 309,
29
+ '中信银行' => 302,
30
+ '中国民生银行' => 305,
31
+ '光大银行' => 303,
32
+ '华夏银行' => 304,
33
+ '北京农村商业银行' => 1418,
34
+ '深圳发展银行' => 307,
35
+ '中国银行' => 104,
36
+ '北京银行' => 403,
37
+ '中国邮政储蓄银行' => 100,
38
+ '上海浦东发展银行' => 310,
39
+ '东亚银行' => 2502,
40
+ '广东发展银行' => 306,
41
+ '南京银行' => 424,
42
+ '上海交通银行' => 301,
43
+ '平安银行' => 410,
44
+ '中国工商银行' => 102,
45
+ '杭州银行' => 423,
46
+ '中国建设银行' => 105,
47
+ '宁波银行' => 408,
48
+ '中国农业银行' => 103,
49
+ '浙商银行' => 316,
50
+ '渤海银行' => 318,
51
+ '上海农村商业银行' => 402,
52
+ '上海银行' => 313
53
+ }.freeze
54
+
55
+ def self.uri_of_pre_pay_euserp(payer_id:, bill_no:, amount_in_cents:, merchant_url:, merchant_para: nil,
56
+ protocol:, merchant_ret_url:, merchant_ret_para: nil,
57
+ options: {})
58
+ generate_pay_link_of('PrePayEUserP',
59
+ payer_id, bill_no, amount_in_cents, merchant_url, merchant_para,
60
+ protocol, merchant_ret_url, merchant_ret_para, nil,
61
+ options)
62
+ end
63
+
64
+ def self.uri_of_pre_pay_c2(bill_no:, amount_in_cents:, merchant_url:, merchant_para: nil,
65
+ merchant_ret_url:, merchant_ret_para: nil, card_bank: nil,
66
+ options: {})
67
+ generate_pay_link_of('PrePayC2',
68
+ nil, bill_no, amount_in_cents, merchant_url, merchant_para,
69
+ nil, merchant_ret_url, merchant_ret_para, card_bank,
70
+ options)
71
+ end
72
+
73
+ def self.uri_of_pre_pay_wap(bill_no:, amount_in_cents:, merchant_url:, merchant_para: nil,
74
+ card_bank: nil,
75
+ options: {})
76
+ generate_pay_link_of('PrePayWAP',
77
+ nil, bill_no, amount_in_cents, merchant_url, merchant_para,
78
+ nil, nil, nil, card_bank,
79
+ options)
80
+ end
81
+
82
+ def self.cmb_pay_message(query_string)
83
+ CmbPay::Message.new query_string
84
+ end
85
+
86
+ private_class_method
87
+
88
+ def self.generate_pay_link_of(pay_type, payer_id, bill_no, amount_in_cents, merchant_url, merchant_para,
89
+ protocol, merchant_ret_url, merchant_ret_para, card_bank, options)
90
+ branch_id = options.delete(:branch_id) || CmbPay.branch_id
91
+ co_no = options.delete(:co_no) || CmbPay.co_no
92
+ co_key = options.delete(:co_key) || CmbPay.co_key
93
+ # 定单号,6位或10位长数字,由商户系统生成,一天内不能重复;
94
+ cmb_bill_no = format('%010d', bill_no.to_i % 10_000_000_000)
95
+ expire_in_minutes = options.delete(:expire_in_minutes) || CmbPay.expire_in_minutes
96
+ pay_in_yuan, pay_in_cent = amount_in_cents.to_i.divmod(100)
97
+ pay_amount = "#{pay_in_yuan}.#{format('%02d', pay_in_cent)}"
98
+ cmb_merchant_para = Service.encode_merchant_para(merchant_para)
99
+ trade_date = options.delete(:trade_date) || Time.now.strftime('%Y%m%d')
100
+ payee_id = options.delete(:payee_id) || CmbPay.default_payee_id
101
+ random = options.delete(:random)
102
+ if protocol.is_a?(Hash) && !payer_id.nil?
103
+ cmb_reserved_xml = {
104
+ 'PNo' => protocol['PNo'],
105
+ 'TS' => protocol['TS'] || Time.now.strftime('%Y%m%d%H%M%S'),
106
+ 'MchNo' => CmbPay.mch_no,
107
+ 'Seq' => protocol['Seq'],
108
+ 'MUID' => payer_id,
109
+ 'URL' => merchant_url,
110
+ 'Para' => cmb_merchant_para
111
+ }.to_xml(root: 'Protocol', skip_instruct: true, skip_types: true, indent: 0)
112
+ else
113
+ cmb_reserved_xml = generate_cmb_card_bank_xml(card_bank)
114
+ payee_id = nil
115
+ end
116
+ m_code = MerchantCode.generate(random: random, strkey: co_key, date: trade_date,
117
+ branch_id: branch_id, co_no: co_no,
118
+ bill_no: cmb_bill_no, amount: pay_amount,
119
+ merchant_para: cmb_merchant_para, merchant_url: merchant_url,
120
+ payer_id: payer_id, payee_id: payee_id,
121
+ reserved: cmb_reserved_xml)
122
+ uri_params = {
123
+ 'BranchID' => branch_id,
124
+ 'CoNo' => co_no,
125
+ 'BillNo' => cmb_bill_no,
126
+ 'Amount' => pay_amount,
127
+ 'Date' => trade_date,
128
+ 'ExpireTimeSpan' => expire_in_minutes,
129
+ 'MerchantUrl' => merchant_url,
130
+ 'MerchantPara' => cmb_merchant_para,
131
+ 'MerchantCode' => m_code
132
+ }
133
+ uri_params['MerchantRetUrl'] = merchant_ret_url unless merchant_ret_url.nil?
134
+ uri_params['MerchantRetPara'] = merchant_ret_para unless merchant_ret_para.nil?
135
+ Service.request_uri(pay_type, uri_params)
136
+ end
137
+
138
+ def self.generate_cmb_card_bank_xml(card_bank)
139
+ return "<CardBank>#{format('%04d', card_bank.to_i)}</CardBank>" if /^\d+$/ =~ card_bank.to_s
140
+ return nil if SUPPORTED_BANK[card_bank].nil?
141
+ "<CardBank>#{format('%04d', SUPPORTED_BANK[card_bank])}</CardBank>"
142
+ end
143
+ end
@@ -0,0 +1,51 @@
1
+ module CmbPay
2
+ module MerchantCode
3
+ class << self
4
+ attr_accessor :random_generator
5
+ end
6
+ @random_generator = Random.new
7
+
8
+ # 产生商户校验码
9
+ #
10
+ # * +random+ - 随机数
11
+ # * +strkey+ - 商户密钥
12
+ # * +date+ - 订单日期
13
+ # * +branch_id+ - 开户分行号
14
+ # * +co_no+ - 商户号
15
+ # * +bill_no+ - 订单号
16
+ # * +amount+ - 订单金额
17
+ # * +merchant_para+ - 商户自定义参数
18
+ # * +merchant_url+ - 商户接受通知的URL
19
+ # * +payer_id+ - 付款方用户标识。用来唯一标识商户的一个用户。长度限制为40字节以内。
20
+ # * +payee_id+ - 收款方的用户标识。生成规则同上。不要求商户提供用户的注册名称,但需要保证一个用户对应一个UserID。
21
+ # * +client_ips+ - 商户取得的客户端IP,如果有多个IP用逗号','分隔。长度限制为64字节。
22
+ # * +goods_type+ - 商品类型编码,长度限制为8字节。
23
+ # * +reserved+ - 保留字段,长度限制为1024字节。
24
+ def self.generate(random: nil, strkey:, date:, branch_id:, co_no:, bill_no:,
25
+ amount:, merchant_para:, merchant_url:,
26
+ payer_id:, payee_id:, client_ips: nil, goods_type: nil, reserved: nil)
27
+ random = random_generator.rand if random.nil?
28
+ last_3 = optional_last_3(client_ips: client_ips, goods_type: goods_type, reserved: reserved)
29
+ combine_part1 = pay_to_in_rc4(random: random, strkey: strkey, payer_id: payer_id, payee_id: payee_id, opt_last_3: last_3)
30
+ combine_part2 = "#{strkey}#{combine_part1}#{date}#{branch_id}#{co_no}#{bill_no}#{amount}#{merchant_para}#{merchant_url}"
31
+ "|#{combine_part1}|#{Sign.sha1_digest(combine_part2)}"
32
+ end
33
+
34
+ private_class_method
35
+
36
+ def self.pay_to_in_rc4(random:, strkey:, payer_id:, payee_id:, opt_last_3:)
37
+ in_data = "#{random}|#{payer_id}<$CmbSplitter$>#{payee_id}#{opt_last_3}"
38
+ key_md5_digest = Sign.md5_key_digest(strkey)
39
+ encrypted = Sign.rc4_encrypt(key_md5_digest, in_data)
40
+ Sign.encode_base64(encrypted)
41
+ end
42
+
43
+ def self.optional_last_3(client_ips: nil, goods_type: nil, reserved: nil)
44
+ r = ''
45
+ r << "<$ClientIP$>#{client_ips}</$ClientIP$>" unless client_ips.to_s == ''
46
+ r << "<$GoodsType$>#{goods_type}</$GoodsType$>" unless goods_type.to_s == ''
47
+ r << "<$Reserved$>#{reserved}</$Reserved$>" unless reserved.to_s == ''
48
+ r
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,68 @@
1
+ module CmbPay
2
+ class Message
3
+ attr_accessor :succeed # 消息成功失败,成功为'Y',失败为'N'
4
+ attr_accessor :co_no # 商户号,6位长数字,由银行在商户开户时确定
5
+ attr_accessor :bill_no # 订单号(由支付命令送来);
6
+ attr_accessor :amount # 实际支付金额(由支付命令送来)
7
+ attr_accessor :date # 订单下单日期(由支付命令送来)
8
+ attr_accessor :merchant_para # 商户自定义传递参数(由支付命令送来)
9
+ attr_accessor :msg # 银行通知用户支付结构消息
10
+
11
+ attr_accessor :branch_id # 分行号
12
+ attr_accessor :bank_date # 银行主机交易日期
13
+ attr_accessor :bank_serial_no # 银行流水号
14
+
15
+ attr_accessor :signature # 通知命令签名
16
+
17
+ attr_reader :query_string # 原始的query_string
18
+
19
+ def initialize(query_string)
20
+ query_string = URI.encode_www_form(query_string) if query_string.is_a? Hash
21
+ @query_string = query_string
22
+
23
+ params = URI.decode_www_form(query_string).to_h
24
+
25
+ @succeed = params['Succeed']
26
+ @co_no = params['CoNo']
27
+ @bill_no = params['BillNo']
28
+ @amount = params['Amount']
29
+ @date = params['Date']
30
+ @merchant_para = params['MerchantPara']
31
+ @msg = params['Msg']
32
+
33
+ # 银行通知用户的支付结果消息。信息的前38个字符格式为:4位分行号+6位商户号+8位银行接受交易的日期+20位银行流水号;
34
+ # 可以利用交易日期+银行流水号+订单号对该订单进行结帐处理
35
+ msg = params['Msg'][0..37]
36
+ @branch_id = msg[0..3]
37
+ @co_no ||= msg[4..9]
38
+ @bank_date = msg[10..17]
39
+ @bank_serial_no = msg[18..37]
40
+
41
+ @signature = params['Signature']
42
+ end
43
+
44
+ def valid?
45
+ Sign::Sha1WithRsa.verify(query_string)
46
+ end
47
+
48
+ def succeed?
49
+ succeed == 'Y'
50
+ end
51
+
52
+ def amount_cents
53
+ (amount.to_f * 100).to_i
54
+ end
55
+
56
+ def order_date
57
+ Date.strptime(date, '%Y%m%d')
58
+ end
59
+
60
+ def payment_date
61
+ Date.strptime(bank_date, '%Y%m%d')
62
+ end
63
+
64
+ def merchant_params
65
+ URI.decode_www_form(merchant_para.tr('|', '&')).to_h
66
+ end
67
+ end
68
+ end
Binary file
@@ -0,0 +1,42 @@
1
+ module CmbPay
2
+ module Service
3
+ GATEWAY_URL = {
4
+ production: {
5
+ NP_BindCard: 'https://mobile.cmbchina.com/mobilehtml/DebitCard/M_NetPay/OneNetRegister/NP_BindCard.aspx',
6
+ PrePayEUserP: 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?PrePayEUserP',
7
+ PrePayC2: 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?PrePayC2',
8
+ PrePayWAP: 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?PrePayWAP'
9
+ },
10
+ test: {
11
+ NP_BindCard: 'http://61.144.248.29:801/mobilehtml/DebitCard/M_NetPay/OneNetRegister/NP_BindCard.aspx',
12
+ PrePayEUserP: 'http://61.144.248.29:801/netpayment/BaseHttp.dll?PrePayEUserP',
13
+ PrePayC2: 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?TestPrePayC2',
14
+ PrePayWAP: 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?TestPrePayWAP'
15
+ }
16
+ }.freeze
17
+
18
+ def self.request_gateway_url(api_action)
19
+ GATEWAY_URL[CmbPay.environment.to_sym][api_action.to_sym]
20
+ end
21
+
22
+ def self.request_uri(api_action, params)
23
+ uri = URI(request_gateway_url(api_action))
24
+ uri.query = if CmbPay.environment.to_sym == :test && api_action.to_sym == :PrePayC2
25
+ "TestPrePayC2?#{URI.encode_www_form(params)}"
26
+ elsif CmbPay.environment.to_sym == :test && api_action.to_sym == :PrePayWAP
27
+ "TestPrePayWAP?#{URI.encode_www_form(params)}"
28
+ else
29
+ "#{uri.query}?#{URI.encode_www_form(params)}"
30
+ end
31
+ uri
32
+ end
33
+
34
+ def self.encode_merchant_para(para)
35
+ if para.nil?
36
+ ''
37
+ else
38
+ para.to_a.collect { |c| "#{c[0]}=#{c[1]}" }.join '|'
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,47 @@
1
+ require 'base64'
2
+ require 'digest/md5'
3
+ require 'openssl' # For RC4/RSA/SHA1
4
+
5
+ module CmbPay
6
+ module Sign
7
+ CMB_PUBLIC_KEY = File.read(File.expand_path('./public.key', __dir__)).freeze
8
+
9
+ module Sha1WithRsa
10
+ def self.verify(param_string)
11
+ pub = OpenSSL::PKey::RSA.new(CMB_PUBLIC_KEY)
12
+ pub.verify('sha1', signature(param_string), plain_text(param_string))
13
+ end
14
+
15
+ private_class_method
16
+
17
+ def self.plain_text(param_string)
18
+ param_string[0, param_string.index('&Signature=')]
19
+ end
20
+
21
+ def self.signature(param_string)
22
+ sign = param_string[param_string.index('&Signature=') + 11, param_string.length - 1]
23
+ sign.split('|').map { |ascii_code| ascii_code.to_i.chr }.join('')
24
+ end
25
+ end
26
+
27
+ # CMB replace '+' with '*' according to standard Base64
28
+ def self.encode_base64(bin)
29
+ ::Base64.strict_encode64(bin).tr('+', '*')
30
+ end
31
+
32
+ def self.md5_key_digest(str_key)
33
+ Digest::MD5.hexdigest(str_key.encode('gb2312')).upcase
34
+ end
35
+
36
+ def self.rc4_encrypt(md5_hash, data)
37
+ cipher = OpenSSL::Cipher.new('RC4')
38
+ cipher.encrypt
39
+ cipher.key = Util.hex_to_binary(md5_hash)
40
+ cipher.update(data.encode('gb2312')) + cipher.final
41
+ end
42
+
43
+ def self.sha1_digest(str)
44
+ OpenSSL::Digest::SHA1.hexdigest(str)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,11 @@
1
+ module CmbPay
2
+ module Util
3
+ def self.hex_to_binary(str)
4
+ str.scan(/../).map(&:hex).map(&:chr).join
5
+ end
6
+
7
+ def self.binary_to_hex(s)
8
+ s.each_byte.map { |b| b.to_s(16).rjust(2, '0') }.join.upcase
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module CmbPay
2
+ VERSION = '0.0.3'.freeze
3
+ end
metadata ADDED
@@ -0,0 +1,168 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cmb_pay
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Eric Guo
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIDeDCCAmCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBBMRMwEQYDVQQDDAplcmlj
14
+ Lmd1b2N6MRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZFgNj
15
+ b20wHhcNMTYwNzExMTUyMTU2WhcNMTcwNzExMTUyMTU2WjBBMRMwEQYDVQQDDApl
16
+ cmljLmd1b2N6MRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZ
17
+ FgNjb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6LSiHb79c6Krm
18
+ TeoHq5fko/QJownbKLtlzHmWCGb6B38mIPqYZBSmfaK9EZr6DTf7TJWB7e/u2+Ep
19
+ bXCqdUxRDbQZig52DvKfljfgOD/YzALHKAckuk/sskvM2pdtRIowJSYAG/Hz2j1d
20
+ mngRZaIDd/09CNttRsgCDXZl+qqsNJKCQWZ3T8OdqmlpwkIxo7llKEQ578fQwgZ3
21
+ 8uE4RLMv8fOdFv0EzGXbRyFLCT2WEAH5Ns2Jv12KKbvYCTICdJ36cSV8hOUZT0fG
22
+ y4FxEXtV9uJVJv3BJ5LE84TWdNiMI1lbFul72p09vebUe2N0Hru3XDnfq69XJ+9e
23
+ nZC9MFk/AgMBAAGjezB5MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
24
+ BBR/fjIribDcJvWvxPh+zv2kFGRAejAfBgNVHREEGDAWgRRlcmljLmd1b2N6QGdt
25
+ YWlsLmNvbTAfBgNVHRIEGDAWgRRlcmljLmd1b2N6QGdtYWlsLmNvbTANBgkqhkiG
26
+ 9w0BAQUFAAOCAQEAIA++aoEJOSbTwQml1cQ+z2psV2R18HKYR7ZM8bGEm9PxGyLt
27
+ DcpqGIL65VctVjqVzD4RTvDrsFhVICL5o0rLYpC4Qm5jBpx+E2s/akJSDsQcOO12
28
+ qk+IDlQHJjhf7DOg0+XAIj1/QTvq8s3QrrD8NRoIvGcAXVzqAafcG9NOGIXpqFS1
29
+ ZdwgixrYZK4p/Z+qIJFiuGvBauegUn0x7WDln52cWXgb+a/oYPBjGtBAZznhSy+R
30
+ R5k6Ma92sW8jupX4cqbSu9rntdVQkNRpoHIrfU0MZT0cKsg/D1zMteylxrO3KMsz
31
+ SPQRv+nrI1J0zevFqb8010heoR8SDyUA0Mm3+Q==
32
+ -----END CERTIFICATE-----
33
+ date: 2016-07-11 00:00:00.000000000 Z
34
+ dependencies:
35
+ - !ruby/object:Gem::Dependency
36
+ name: http
37
+ requirement: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: 1.0.4
42
+ - - "<"
43
+ - !ruby/object:Gem::Version
44
+ version: '3'
45
+ type: :runtime
46
+ prerelease: false
47
+ version_requirements: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: 1.0.4
52
+ - - "<"
53
+ - !ruby/object:Gem::Version
54
+ version: '3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 4.2.6
62
+ - - "<"
63
+ - !ruby/object:Gem::Version
64
+ version: 5.1.x
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: 4.2.6
72
+ - - "<"
73
+ - !ruby/object:Gem::Version
74
+ version: 5.1.x
75
+ - !ruby/object:Gem::Dependency
76
+ name: bundler
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '1.12'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '1.12'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rake
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '11.2'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '11.2'
103
+ - !ruby/object:Gem::Dependency
104
+ name: rspec
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '3.5'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '3.5'
117
+ description: Helping rubyist integration with cmb (China Merchants Bank) payment service
118
+ (招商银行一网通支付) easier.
119
+ email:
120
+ - eric.guocz@gmail.com
121
+ executables: []
122
+ extensions: []
123
+ extra_rdoc_files: []
124
+ files:
125
+ - ".gitignore"
126
+ - ".idea/vcs.xml"
127
+ - ".rspec"
128
+ - ".rubocop.yml"
129
+ - ".travis.yml"
130
+ - LICENSE.txt
131
+ - README.md
132
+ - bin/console
133
+ - bin/setup
134
+ - certs/Eric-Guo.pem
135
+ - checksum/cmb_pay-0.0.3.gem.sha512
136
+ - lib/cmb_pay.rb
137
+ - lib/cmb_pay/merchant_code.rb
138
+ - lib/cmb_pay/message.rb
139
+ - lib/cmb_pay/public.key
140
+ - lib/cmb_pay/service.rb
141
+ - lib/cmb_pay/sign.rb
142
+ - lib/cmb_pay/util.rb
143
+ - lib/cmb_pay/version.rb
144
+ homepage: https://github.com/bayetech/cmb_pay
145
+ licenses:
146
+ - MIT
147
+ metadata: {}
148
+ post_install_message:
149
+ rdoc_options: []
150
+ require_paths:
151
+ - lib
152
+ required_ruby_version: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - "~>"
155
+ - !ruby/object:Gem::Version
156
+ version: '2.1'
157
+ required_rubygems_version: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ requirements: []
163
+ rubyforge_project:
164
+ rubygems_version: 2.6.4
165
+ signing_key:
166
+ specification_version: 4
167
+ summary: An unofficial cmb (China Merchants Bank) pay gem.
168
+ test_files: []
metadata.gz.sig ADDED
Binary file