cmb_pay 0.0.3 → 0.1.0
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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.codeclimate.yml +18 -0
- data/.rubocop.yml +4 -1
- data/README.md +8 -0
- data/lib/cmb_pay/message/bill_records_message.rb +85 -0
- data/lib/cmb_pay/message/pay_message.rb +81 -0
- data/lib/cmb_pay/message/refund_order_message.rb +34 -0
- data/lib/cmb_pay/message/single_order_message.rb +45 -0
- data/lib/cmb_pay/service.rb +4 -2
- data/lib/cmb_pay/util.rb +15 -2
- data/lib/cmb_pay/version.rb +1 -1
- data/lib/cmb_pay.rb +102 -22
- data.tar.gz.sig +0 -0
- metadata +7 -24
- metadata.gz.sig +0 -0
- data/checksum/cmb_pay-0.0.3.gem.sha512 +0 -1
- data/lib/cmb_pay/message.rb +0 -68
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7cb6e2e16c168adda66116084b9baee9885df88f
|
4
|
+
data.tar.gz: 538974a366f22d25f637d139d1a6422ff368f773
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 037d0f1f208ca53ba2bfb47d09c87f68d7ee5992e3e63e50d5d956b3bac73a8badf359839049803df0dcc441b94e8d2555ae5ff02715d1da03b5f1daacc06b3a
|
7
|
+
data.tar.gz: a8bfbee55dd68369f559f4f5c013cb330076046481f5948d3542f913791e2f760d68501a79b5f11fce929412084c75c5785cc8dfb19aed6e1afb0759ad6eaf9e
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/.codeclimate.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
---
|
2
|
+
engines:
|
3
|
+
duplication:
|
4
|
+
enabled: true
|
5
|
+
exclude_fingerprints:
|
6
|
+
- 02bd778d4f4ff959546967a3bcaffc6e
|
7
|
+
config:
|
8
|
+
languages:
|
9
|
+
- ruby
|
10
|
+
fixme:
|
11
|
+
enabled: true
|
12
|
+
rubocop:
|
13
|
+
enabled: true
|
14
|
+
ratings:
|
15
|
+
paths:
|
16
|
+
- "**.rb"
|
17
|
+
exclude_paths:
|
18
|
+
- spec/
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -3,6 +3,14 @@ CMB Pay [![Gem Version][version-badge]][rubygems] [![Build Status][travis-badge]
|
|
3
3
|
|
4
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
5
|
|
6
|
+
## Feature
|
7
|
+
|
8
|
+
* Payment URL generation for Web, App and WAP(Mobile Web).
|
9
|
+
* CMB Bank notification payment callback parse and verify.
|
10
|
+
* Direct refund API.
|
11
|
+
* Single order query.
|
12
|
+
* Multi orders query by transact/merchant date/settled date.
|
13
|
+
|
6
14
|
## Installation
|
7
15
|
|
8
16
|
Add this line to your application's Gemfile:
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'rexml/streamlistener'
|
2
|
+
|
3
|
+
module CmbPay
|
4
|
+
class BillRecordsMessage
|
5
|
+
attr_reader :raw_http_response, :code, :error_message,
|
6
|
+
:query_loop_flag, :query_loop_pos, :bill_records
|
7
|
+
def initialize(http_response)
|
8
|
+
@raw_http_response = http_response
|
9
|
+
return unless http_response.code == 200
|
10
|
+
@bill_records = []
|
11
|
+
|
12
|
+
REXML::Document.parse_stream(http_response.body, self)
|
13
|
+
end
|
14
|
+
|
15
|
+
def tag_start(name, _attributes)
|
16
|
+
case name
|
17
|
+
when 'Head' then @in_head = true
|
18
|
+
when 'Body' then @in_body = true
|
19
|
+
when 'BllRecord' then @current_bill_record = {}
|
20
|
+
else
|
21
|
+
@current_element_name = name
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def tag_end(name)
|
26
|
+
case name
|
27
|
+
when 'Head' then @in_head = false
|
28
|
+
when 'Body' then @in_body = false
|
29
|
+
when 'BllRecord' then @bill_records << @current_bill_record
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def text(text)
|
34
|
+
if @in_head
|
35
|
+
case @current_element_name
|
36
|
+
when 'Code' then @code = text
|
37
|
+
when 'ErrMsg' then @error_message = text
|
38
|
+
end
|
39
|
+
elsif @in_body
|
40
|
+
case @current_element_name
|
41
|
+
# 续传标记(采用多次通讯方式续传时使用) 默认值为’N’,表示没有后续数据包,’Y’表示仍有后续的通讯包
|
42
|
+
when 'QryLopFlg' then @query_loop_flag = text
|
43
|
+
# 续传包请求数据
|
44
|
+
when 'QryLopBlk' then @query_loop_pos = text
|
45
|
+
# 商户定单号
|
46
|
+
when 'BillNo' then @current_bill_record[:bill_no] = text
|
47
|
+
# 商户日期
|
48
|
+
when 'MchDate' then @current_bill_record[:merchant_date] = text
|
49
|
+
# 结算日期
|
50
|
+
when 'StlDate' then @current_bill_record[:settled_date] = text
|
51
|
+
# 订单状态
|
52
|
+
when 'BillState' then @current_bill_record[:bill_state] = text
|
53
|
+
# 订单金额
|
54
|
+
when 'BillAmount' then @current_bill_record[:bill_amount] = text
|
55
|
+
# 手续费
|
56
|
+
when 'FeeAmount' then @current_bill_record[:fee_amount] = text
|
57
|
+
# 卡类型
|
58
|
+
when 'CardType' then @current_bill_record[:card_type] = text
|
59
|
+
# 交易流水号
|
60
|
+
when 'BillRfn' then @current_bill_record[:bill_ref_no] = text
|
61
|
+
# 实扣金额
|
62
|
+
when 'StlAmount' then @current_bill_record[:settled_amount] = text
|
63
|
+
# 优惠金额
|
64
|
+
when 'DecPayAmount' then @current_bill_record[:discount_pay_amount] = text
|
65
|
+
# 订单类型:A表示二维码支付订单,B表示普通订单
|
66
|
+
when 'BillType' then @current_bill_record[:bill_type] = text
|
67
|
+
# 如果订单类型为A,下述字段才存在
|
68
|
+
when 'Addressee' then @current_bill_record[:addressee] = text # 收货人姓名
|
69
|
+
when 'Country' then @current_bill_record[:country] = text # 国家
|
70
|
+
when 'Province' then @current_bill_record[:province] = text # 省份
|
71
|
+
when 'City' then @current_bill_record[:city] = text # 城市
|
72
|
+
when 'Address' then @current_bill_record[:address] = text # 街道地址
|
73
|
+
when 'Mobile' then @current_bill_record[:mobile] = text # 手机号
|
74
|
+
when 'Telephone' then @current_bill_record[:telephone] = text # 固定电话
|
75
|
+
when 'ZipCode' then @current_bill_record[:zipcode] = text # 邮编
|
76
|
+
when 'GoodsURL' then @current_bill_record[:goodsurl] = text # 商品详情链接
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def succeed?
|
82
|
+
code.nil? && error_message.nil?
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module CmbPay
|
2
|
+
class PayMessage
|
3
|
+
attr_reader :succeed # 消息成功失败,成功为'Y',失败为'N'
|
4
|
+
attr_reader :co_no # 商户号,6位长数字,由银行在商户开户时确定
|
5
|
+
attr_reader :bill_no # 订单号(由支付命令送来);
|
6
|
+
attr_reader :amount # 实际支付金额(由支付命令送来)
|
7
|
+
attr_reader :date # 订单下单日期(由支付命令送来)
|
8
|
+
attr_reader :merchant_para # 商户自定义传递参数(由支付命令送来)
|
9
|
+
attr_reader :msg # 银行通知用户支付结构消息
|
10
|
+
attr_reader :discount_flag # 当前订单是否有优惠,Y:有优惠 N:无优惠。
|
11
|
+
attr_reader :discount_amt # 优惠金额,格式:xxxx.xx,当有优惠的时候,
|
12
|
+
# 实际用户支付的是amount - discount_amt的金额到商户账号。
|
13
|
+
|
14
|
+
attr_reader :branch_id # 分行号
|
15
|
+
attr_reader :bank_date # 银行主机交易日期
|
16
|
+
attr_reader :bank_serial_no # 银行流水号
|
17
|
+
|
18
|
+
attr_reader :signature # 通知命令签名
|
19
|
+
|
20
|
+
attr_reader :query_string # 原始的query_string
|
21
|
+
|
22
|
+
def initialize(query_string)
|
23
|
+
query_string = URI.encode_www_form(query_string) if query_string.is_a? Hash
|
24
|
+
@query_string = query_string
|
25
|
+
|
26
|
+
params = URI.decode_www_form(query_string).to_h
|
27
|
+
|
28
|
+
@succeed = params['Succeed']
|
29
|
+
@co_no = params['CoNo']
|
30
|
+
@bill_no = params['BillNo']
|
31
|
+
@amount = params['Amount']
|
32
|
+
@date = params['Date']
|
33
|
+
@merchant_para = params['MerchantPara']
|
34
|
+
@msg = params['Msg']
|
35
|
+
@discount_flag = params['DiscountFlag']
|
36
|
+
@discount_amt = params['DiscountAmt']
|
37
|
+
|
38
|
+
# 银行通知用户的支付结果消息。信息的前38个字符格式为:4位分行号+6位商户号+8位银行接受交易的日期+20位银行流水号;
|
39
|
+
# 可以利用交易日期+银行流水号+订单号对该订单进行结帐处理
|
40
|
+
msg = params['Msg'][0..37]
|
41
|
+
@branch_id = msg[0..3]
|
42
|
+
@co_no ||= msg[4..9]
|
43
|
+
@bank_date = msg[10..17]
|
44
|
+
@bank_serial_no = msg[18..37]
|
45
|
+
|
46
|
+
@signature = params['Signature']
|
47
|
+
end
|
48
|
+
|
49
|
+
def valid?
|
50
|
+
Sign::Sha1WithRsa.verify(query_string)
|
51
|
+
end
|
52
|
+
|
53
|
+
def succeed?
|
54
|
+
succeed == 'Y'
|
55
|
+
end
|
56
|
+
|
57
|
+
def discount?
|
58
|
+
discount_flag == 'Y'
|
59
|
+
end
|
60
|
+
|
61
|
+
def amount_cents
|
62
|
+
(amount.to_f * 100).to_i
|
63
|
+
end
|
64
|
+
|
65
|
+
def discount_amount_cents
|
66
|
+
(discount_amt.to_f * 100).to_i
|
67
|
+
end
|
68
|
+
|
69
|
+
def order_date
|
70
|
+
Date.strptime(date, '%Y%m%d')
|
71
|
+
end
|
72
|
+
|
73
|
+
def payment_date
|
74
|
+
Date.strptime(bank_date, '%Y%m%d')
|
75
|
+
end
|
76
|
+
|
77
|
+
def merchant_params
|
78
|
+
URI.decode_www_form(merchant_para.tr('|', '&')).to_h
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
3
|
+
module CmbPay
|
4
|
+
class RefundOrderMessage
|
5
|
+
attr_reader :raw_http_response, :code, :error_message,
|
6
|
+
:refund_no, # 银行退款流水号
|
7
|
+
:bank_seq_no, # 银行流水号
|
8
|
+
:amount, # 退款金额
|
9
|
+
:date, # 银行交易日期YYYYMMDD
|
10
|
+
:time # 银行交易时间hhmmss
|
11
|
+
|
12
|
+
def initialize(http_response)
|
13
|
+
@raw_http_response = http_response
|
14
|
+
return unless http_response.code == 200
|
15
|
+
|
16
|
+
document_root = REXML::Document.new(http_response.body.to_s).root
|
17
|
+
head = document_root.elements['Head']
|
18
|
+
@code = head.elements['Code'].text
|
19
|
+
@error_message = head.elements['ErrMsg'].text
|
20
|
+
return unless succeed?
|
21
|
+
|
22
|
+
body = document_root.elements['Body']
|
23
|
+
@refund_no = body.elements['RefundNo'].text
|
24
|
+
@bank_seq_no = body.elements['BankSeqNo'].text
|
25
|
+
@amount = body.elements['Amount'].text
|
26
|
+
@date = body.elements['Date'].text
|
27
|
+
@time = body.elements['Time'].text
|
28
|
+
end
|
29
|
+
|
30
|
+
def succeed?
|
31
|
+
code.nil? && error_message.nil?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
3
|
+
module CmbPay
|
4
|
+
class SingleOrderMessage
|
5
|
+
attr_reader :raw_http_response, :code, :error_message,
|
6
|
+
:bill_no, # 定单号
|
7
|
+
:amount, # 实扣金额
|
8
|
+
:accept_date, # 受理日期
|
9
|
+
:accept_time, # 受理时间
|
10
|
+
:bill_amount, # 定单金额
|
11
|
+
:status, # 定单状态,0-已结帐,1-已撤销,2-部分结帐,4-未结帐,7-冻结交易-已经冻结金额已经全部结账 8-冻结交易,冻结金额只结帐了一部分
|
12
|
+
:card_type, # 卡类型:02:一卡通 03:信用卡
|
13
|
+
:fee, # 手续费
|
14
|
+
:merchant_para, # 商户自定义参数。里面的特殊字符已经转码
|
15
|
+
:card_no, # 卡号(部分数字用“*”掩盖)
|
16
|
+
:bank_seq_no # 银行流水号
|
17
|
+
def initialize(http_response)
|
18
|
+
@raw_http_response = http_response
|
19
|
+
return unless http_response.code == 200
|
20
|
+
|
21
|
+
document_root = REXML::Document.new(http_response.body.to_s).root
|
22
|
+
head = document_root.elements['Head']
|
23
|
+
@code = head.elements['Code'].text
|
24
|
+
@error_message = head.elements['ErrMsg'].text
|
25
|
+
return unless succeed?
|
26
|
+
|
27
|
+
body = document_root.elements['Body']
|
28
|
+
@bill_no = body.elements['BillNo'].text
|
29
|
+
@amount = body.elements['Amount'].text
|
30
|
+
@accept_date = body.elements['AcceptDate'].text
|
31
|
+
@accept_time = body.elements['AcceptTime'].text
|
32
|
+
@bill_amount = body.elements['BillAmount'].text
|
33
|
+
@status = body.elements['Status'].text
|
34
|
+
@card_type = body.elements['CardType'].text
|
35
|
+
@fee = body.elements['Fee'].text
|
36
|
+
@merchant_para = body.elements['MerchantPara'].text
|
37
|
+
@card_no = body.elements['CardNo'].text
|
38
|
+
@bank_seq_no = body.elements['BankSeqNo'].text
|
39
|
+
end
|
40
|
+
|
41
|
+
def succeed?
|
42
|
+
code.nil? && error_message.nil?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/cmb_pay/service.rb
CHANGED
@@ -5,13 +5,15 @@ module CmbPay
|
|
5
5
|
NP_BindCard: 'https://mobile.cmbchina.com/mobilehtml/DebitCard/M_NetPay/OneNetRegister/NP_BindCard.aspx',
|
6
6
|
PrePayEUserP: 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?PrePayEUserP',
|
7
7
|
PrePayC2: 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?PrePayC2',
|
8
|
-
PrePayWAP: 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?PrePayWAP'
|
8
|
+
PrePayWAP: 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?PrePayWAP',
|
9
|
+
DirectRequestX: 'https://payment.ebank.cmbchina.com/netpayment/basehttp.dll?DirectRequestX'
|
9
10
|
},
|
10
11
|
test: {
|
11
12
|
NP_BindCard: 'http://61.144.248.29:801/mobilehtml/DebitCard/M_NetPay/OneNetRegister/NP_BindCard.aspx',
|
12
13
|
PrePayEUserP: 'http://61.144.248.29:801/netpayment/BaseHttp.dll?PrePayEUserP',
|
13
14
|
PrePayC2: 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?TestPrePayC2',
|
14
|
-
PrePayWAP: 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?TestPrePayWAP'
|
15
|
+
PrePayWAP: 'https://netpay.cmbchina.com/netpayment/BaseHttp.dll?TestPrePayWAP',
|
16
|
+
DirectRequestX: 'http://218.17.27.197/netpayment/basehttp.dll?DirectRequestX'
|
15
17
|
}
|
16
18
|
}.freeze
|
17
19
|
|
data/lib/cmb_pay/util.rb
CHANGED
@@ -1,11 +1,24 @@
|
|
1
1
|
module CmbPay
|
2
2
|
module Util
|
3
|
+
MILLISECONDS_SINCE_UNIX_EPOCH = Time.new(2000, 1, 1).to_i * 1000
|
4
|
+
|
3
5
|
def self.hex_to_binary(str)
|
4
|
-
str.
|
6
|
+
[str].pack('H*')
|
5
7
|
end
|
6
8
|
|
7
9
|
def self.binary_to_hex(s)
|
8
|
-
s.
|
10
|
+
s.unpack('H*')[0].upcase
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.cmb_timestamp(t: nil)
|
14
|
+
return t if t.is_a?(Integer)
|
15
|
+
t = Time.now if t.nil?
|
16
|
+
t.to_i * 1000 + (t.usec / 1000) - MILLISECONDS_SINCE_UNIX_EPOCH
|
17
|
+
end
|
18
|
+
|
19
|
+
# 招行定单号,6位或10位长数字,由商户系统生成,一天内不能重复
|
20
|
+
def self.cmb_bill_no(bill_no)
|
21
|
+
format('%010d', bill_no.to_i % 10_000_000_000)
|
9
22
|
end
|
10
23
|
end
|
11
24
|
end
|
data/lib/cmb_pay/version.rb
CHANGED
data/lib/cmb_pay.rb
CHANGED
@@ -1,25 +1,33 @@
|
|
1
1
|
require 'date'
|
2
2
|
require 'uri'
|
3
|
-
require '
|
3
|
+
require 'http' # https://github.com/httprb/http
|
4
4
|
require 'cmb_pay/version'
|
5
5
|
require 'cmb_pay/util'
|
6
6
|
require 'cmb_pay/sign'
|
7
7
|
require 'cmb_pay/merchant_code'
|
8
|
-
require 'cmb_pay/message'
|
9
8
|
require 'cmb_pay/service'
|
10
9
|
|
11
10
|
module CmbPay
|
11
|
+
autoload(:PayMessage, File.expand_path('cmb_pay/message/pay_message', __dir__))
|
12
|
+
autoload(:RefundOrderMessage, File.expand_path('cmb_pay/message/refund_order_message', __dir__))
|
13
|
+
autoload(:SingleOrderMessage, File.expand_path('cmb_pay/message/single_order_message', __dir__))
|
14
|
+
autoload(:BillRecordsMessage, File.expand_path('cmb_pay/message/bill_records_message', __dir__))
|
15
|
+
|
12
16
|
class << self
|
13
|
-
attr_accessor :branch_id
|
14
|
-
attr_accessor :co_no
|
15
|
-
attr_accessor :co_key
|
16
|
-
attr_accessor :mch_no
|
17
|
-
attr_accessor :
|
18
|
-
attr_accessor :
|
19
|
-
attr_accessor :
|
17
|
+
attr_accessor :branch_id # 开户分行号
|
18
|
+
attr_accessor :co_no # 支付商户号/收单商户号
|
19
|
+
attr_accessor :co_key # 商户校验码/商户密钥,测试环境为空
|
20
|
+
attr_accessor :mch_no # 协议商户企业编号,或者说是8位虚拟企业网银编号
|
21
|
+
attr_accessor :operator # 操作员号,一般是9999
|
22
|
+
attr_accessor :operator_password # 操作员的密码,默认是支付商户号,但建议修改。
|
23
|
+
attr_accessor :expire_in_minutes # 会话有效时间
|
24
|
+
attr_accessor :environment # 调用的招商银行支付环境,默认生产,测试填test
|
25
|
+
attr_accessor :default_payee_id # 默认收款方的用户标识
|
20
26
|
end
|
21
27
|
@co_key = ''
|
22
28
|
@mch_no = ''
|
29
|
+
@operator = '9999'
|
30
|
+
@operator_password = ''
|
23
31
|
@expire_in_minutes = 30
|
24
32
|
@environment = :production
|
25
33
|
|
@@ -79,19 +87,99 @@ module CmbPay
|
|
79
87
|
options)
|
80
88
|
end
|
81
89
|
|
82
|
-
def self.
|
83
|
-
|
90
|
+
def self.pay_message(query_string)
|
91
|
+
PayMessage.new query_string
|
92
|
+
end
|
93
|
+
|
94
|
+
# 退款接口
|
95
|
+
def self.refund_no_dup(bill_no:, refund_no:, refund_amount_in_cents:, memo:,
|
96
|
+
bill_date: nil, operator: nil, operator_password: nil,
|
97
|
+
branch_id: nil, co_no: nil, co_key: nil, time_stamp: nil)
|
98
|
+
refund_in_yuan, refund_in_cent = refund_amount_in_cents.to_i.divmod(100)
|
99
|
+
refund_amount = "#{refund_in_yuan}.#{format('%02d', refund_in_cent)}"
|
100
|
+
desc = memo.encode(xml: :text)
|
101
|
+
bill_date = Time.now.strftime('%Y%m%d') if bill_date.nil?
|
102
|
+
head_inner_xml = build_direct_request_x_head('Refund_No_Dup', branch_id, co_no, time_stamp,
|
103
|
+
with_operator: true, operator: operator, operator_password: operator_password)
|
104
|
+
body_inner_xml = "<Date>#{bill_date}</Date><BillNo>#{Util.cmb_bill_no(bill_no)}</BillNo><RefundNo>#{Util.cmb_bill_no(refund_no)}</RefundNo><Amount>#{refund_amount}</Amount><Desc>#{desc}</Desc>"
|
105
|
+
http_response = hash_and_direct_request_x(co_key, head_inner_xml, body_inner_xml)
|
106
|
+
RefundOrderMessage.new(http_response)
|
107
|
+
end
|
108
|
+
|
109
|
+
# 单笔定单查询接口
|
110
|
+
def self.query_single_order(bill_no:, trade_date: nil,
|
111
|
+
branch_id: nil, co_no: nil, co_key: nil, time_stamp: nil)
|
112
|
+
trade_date = Time.now.strftime('%Y%m%d') if trade_date.nil?
|
113
|
+
head_inner_xml = build_direct_request_x_head('QuerySingleOrder', branch_id, co_no, time_stamp)
|
114
|
+
body_inner_xml = "<Date>#{trade_date}</Date><BillNo>#{Util.cmb_bill_no(bill_no)}</BillNo>"
|
115
|
+
http_response = hash_and_direct_request_x(co_key, head_inner_xml, body_inner_xml)
|
116
|
+
SingleOrderMessage.new(http_response)
|
117
|
+
end
|
118
|
+
|
119
|
+
# 商户入账查询接口
|
120
|
+
# pos: 当<QryLopFlg>Y</QryLopFlg>续传标记为Y时(表示仍有后续的通讯包,采用多次通讯方式续传时使用)
|
121
|
+
# 填写<QryLopBlk>续传包请求数据</QryLopBlk>中的数据
|
122
|
+
def self.query_transact(begin_date:, end_date:, count:, operator: nil, pos: nil,
|
123
|
+
branch_id: nil, co_no: nil, co_key: nil, time_stamp: nil)
|
124
|
+
query_order_by_cmb_command('QueryTransact', begin_date, end_date, count, operator, pos, branch_id, co_no, co_key, time_stamp)
|
125
|
+
end
|
126
|
+
|
127
|
+
# 按商户日期查询已结账订单接口
|
128
|
+
def self.query_settled_order_by_merchant_date(begin_date:, end_date:, count:, operator: nil, pos: nil,
|
129
|
+
branch_id: nil, co_no: nil, co_key: nil, time_stamp: nil)
|
130
|
+
query_order_by_cmb_command('QuerySettledOrderByMerchantDate', begin_date, end_date, count, operator, pos, branch_id, co_no, co_key, time_stamp)
|
131
|
+
end
|
132
|
+
|
133
|
+
# 按结账日期查询已结账订单接口
|
134
|
+
def self.query_settled_order_by_settled_date(begin_date:, end_date:, count:, operator: nil, pos: nil,
|
135
|
+
branch_id: nil, co_no: nil, co_key: nil, time_stamp: nil)
|
136
|
+
query_order_by_cmb_command('QuerySettledOrderBySettledDate', begin_date, end_date, count, operator, pos, branch_id, co_no, co_key, time_stamp)
|
84
137
|
end
|
85
138
|
|
86
139
|
private_class_method
|
87
140
|
|
141
|
+
def self.query_order_by_cmb_command(cmb_command, begin_date, end_date, count, operator, pos,
|
142
|
+
branch_id, co_no, co_key, time_stamp)
|
143
|
+
head_inner_xml = build_direct_request_x_head(cmb_command, branch_id, co_no, time_stamp)
|
144
|
+
body_inner_xml = build_direct_request_x_query_body(begin_date, end_date, count, operator, pos)
|
145
|
+
http_response = hash_and_direct_request_x(co_key, head_inner_xml, body_inner_xml)
|
146
|
+
BillRecordsMessage.new(http_response)
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.build_direct_request_x_head(cmb_command, branch_id, co_no, time_stamp,
|
150
|
+
with_operator: false, operator: nil, operator_password: nil)
|
151
|
+
branch_id = CmbPay.branch_id if branch_id.nil?
|
152
|
+
co_no = CmbPay.co_no if co_no.nil?
|
153
|
+
if with_operator
|
154
|
+
operator = CmbPay.operator if operator.nil?
|
155
|
+
operator_password = CmbPay.operator_password if operator_password.nil?
|
156
|
+
"<BranchNo>#{branch_id}</BranchNo><MerchantNo>#{co_no}</MerchantNo><Operator>#{operator}</Operator><Password>#{operator_password}</Password><TimeStamp>#{Util.cmb_timestamp(t: time_stamp)}</TimeStamp><Command>#{cmb_command}</Command>"
|
157
|
+
else
|
158
|
+
"<BranchNo>#{branch_id}</BranchNo><MerchantNo>#{co_no}</MerchantNo><TimeStamp>#{Util.cmb_timestamp(t: time_stamp)}</TimeStamp><Command>#{cmb_command}</Command>"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def self.build_direct_request_x_query_body(begin_date, end_date, count, operator, pos)
|
163
|
+
operator = '9999' if operator.nil?
|
164
|
+
begin_date = begin_date.strftime('%Y%m%d') unless begin_date.is_a?(String)
|
165
|
+
end_date = end_date.strftime('%Y%m%d') unless end_date.is_a?(String)
|
166
|
+
"<BeginDate>#{begin_date}</BeginDate><EndDate>#{end_date}</EndDate><Count>#{count}</Count><Operator>#{operator}</Operator><pos>#{pos}</pos>"
|
167
|
+
end
|
168
|
+
|
169
|
+
def self.hash_and_direct_request_x(co_key, head_inner_xml, body_inner_xml)
|
170
|
+
co_key = CmbPay.co_key if co_key.nil?
|
171
|
+
hash_input = "#{co_key}#{head_inner_xml}#{body_inner_xml}"
|
172
|
+
hash_xml = "<Hash>#{Sign.sha1_digest(hash_input)}</Hash>"
|
173
|
+
request_xml = "<Request><Head>#{head_inner_xml}</Head><Body>#{body_inner_xml}</Body>#{hash_xml}</Request>"
|
174
|
+
HTTP.post(Service.request_gateway_url(:DirectRequestX), form: { 'Request' => request_xml })
|
175
|
+
end
|
176
|
+
|
88
177
|
def self.generate_pay_link_of(pay_type, payer_id, bill_no, amount_in_cents, merchant_url, merchant_para,
|
89
178
|
protocol, merchant_ret_url, merchant_ret_para, card_bank, options)
|
90
179
|
branch_id = options.delete(:branch_id) || CmbPay.branch_id
|
91
180
|
co_no = options.delete(:co_no) || CmbPay.co_no
|
92
181
|
co_key = options.delete(:co_key) || CmbPay.co_key
|
93
|
-
|
94
|
-
cmb_bill_no = format('%010d', bill_no.to_i % 10_000_000_000)
|
182
|
+
cmb_bill_no = Util.cmb_bill_no(bill_no)
|
95
183
|
expire_in_minutes = options.delete(:expire_in_minutes) || CmbPay.expire_in_minutes
|
96
184
|
pay_in_yuan, pay_in_cent = amount_in_cents.to_i.divmod(100)
|
97
185
|
pay_amount = "#{pay_in_yuan}.#{format('%02d', pay_in_cent)}"
|
@@ -100,15 +188,7 @@ module CmbPay
|
|
100
188
|
payee_id = options.delete(:payee_id) || CmbPay.default_payee_id
|
101
189
|
random = options.delete(:random)
|
102
190
|
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)
|
191
|
+
cmb_reserved_xml = "<Protocol><PNo>#{protocol['PNo']}</PNo><TS>#{protocol['TS'] || Time.now.strftime('%Y%m%d%H%M%S')}</TS><MchNo>#{CmbPay.mch_no}</MchNo><Seq>#{protocol['Seq']}</Seq><MUID>#{payer_id}</MUID><URL>#{merchant_url}</URL><Para>#{cmb_merchant_para}</Para></Protocol>"
|
112
192
|
else
|
113
193
|
cmb_reserved_xml = generate_cmb_card_bank_xml(card_bank)
|
114
194
|
payee_id = nil
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cmb_pay
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Guo
|
@@ -30,7 +30,7 @@ cert_chain:
|
|
30
30
|
R5k6Ma92sW8jupX4cqbSu9rntdVQkNRpoHIrfU0MZT0cKsg/D1zMteylxrO3KMsz
|
31
31
|
SPQRv+nrI1J0zevFqb8010heoR8SDyUA0Mm3+Q==
|
32
32
|
-----END CERTIFICATE-----
|
33
|
-
date: 2016-
|
33
|
+
date: 2016-08-08 00:00:00.000000000 Z
|
34
34
|
dependencies:
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
36
|
name: http
|
@@ -52,26 +52,6 @@ dependencies:
|
|
52
52
|
- - "<"
|
53
53
|
- !ruby/object:Gem::Version
|
54
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
55
|
- !ruby/object:Gem::Dependency
|
76
56
|
name: bundler
|
77
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,6 +102,7 @@ executables: []
|
|
122
102
|
extensions: []
|
123
103
|
extra_rdoc_files: []
|
124
104
|
files:
|
105
|
+
- ".codeclimate.yml"
|
125
106
|
- ".gitignore"
|
126
107
|
- ".idea/vcs.xml"
|
127
108
|
- ".rspec"
|
@@ -132,10 +113,12 @@ files:
|
|
132
113
|
- bin/console
|
133
114
|
- bin/setup
|
134
115
|
- certs/Eric-Guo.pem
|
135
|
-
- checksum/cmb_pay-0.0.3.gem.sha512
|
136
116
|
- lib/cmb_pay.rb
|
137
117
|
- lib/cmb_pay/merchant_code.rb
|
138
|
-
- lib/cmb_pay/message.rb
|
118
|
+
- lib/cmb_pay/message/bill_records_message.rb
|
119
|
+
- lib/cmb_pay/message/pay_message.rb
|
120
|
+
- lib/cmb_pay/message/refund_order_message.rb
|
121
|
+
- lib/cmb_pay/message/single_order_message.rb
|
139
122
|
- lib/cmb_pay/public.key
|
140
123
|
- lib/cmb_pay/service.rb
|
141
124
|
- lib/cmb_pay/sign.rb
|
metadata.gz.sig
CHANGED
Binary file
|
@@ -1 +0,0 @@
|
|
1
|
-
49dec10bce7bd6a94b0b80e6539e209a2297bcc6a6fc23699d78d5f2cdd222b36a7ce4aa6f18cfd27afed3d326891938d7279c5e41fecad986f9ea5f6af49443
|
data/lib/cmb_pay/message.rb
DELETED
@@ -1,68 +0,0 @@
|
|
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
|