cmb_pay 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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