allinpay_cnp 0.1.4 → 0.1.6
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
- data/README.md +50 -8
- data/lib/allinpay_cnp/client.rb +28 -12
- data/lib/allinpay_cnp/request.rb +5 -5
- data/lib/allinpay_cnp/response.rb +1 -1
- data/lib/allinpay_cnp/signature.rb +4 -4
- data/lib/allinpay_cnp/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fafe64e400a551831d156112734279e2fb85e79be6501313be8207a1cfec73f7
|
|
4
|
+
data.tar.gz: 2622d961e762fab513b071796956dc60475e43271541f862c08f3a11b813b725
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eb660034680377320d589e3608ff84df6f2225276ae57bf7711648600b3df5d6d61973c539a925cb7b05aa986a161e293c62481bffd3471d04f7ddc7a1dd385b
|
|
7
|
+
data.tar.gz: 5da018a9be520a162b9dbcfa76807ca15fe2b63baa607aff17d0b17a3ac65d50cc8f914296a59fb52a031e7cf884446f67e77c0734ccf3db95b572dda0380ab8
|
data/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
### Gemfile
|
|
8
8
|
|
|
9
9
|
```ruby
|
|
10
|
-
gem 'allinpay_cnp',
|
|
10
|
+
gem 'allinpay_cnp', '~> 0.1.6'
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
### 本地安装
|
|
@@ -15,7 +15,7 @@ gem 'allinpay_cnp', git: 'https://github.com/your-org/allinpay_cnp'
|
|
|
15
15
|
```bash
|
|
16
16
|
cd allinpay_cnp
|
|
17
17
|
gem build allinpay_cnp.gemspec
|
|
18
|
-
gem install allinpay_cnp-0.1.
|
|
18
|
+
gem install allinpay_cnp-0.1.6.gem
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
## 配置
|
|
@@ -29,6 +29,7 @@ AllinpayCnp.configure do |config|
|
|
|
29
29
|
config.merchant_id = Rails.application.credentials.dig(:allinpay, :merchant_id)
|
|
30
30
|
config.private_key = Rails.application.credentials.dig(:allinpay, :private_key)
|
|
31
31
|
config.public_key = Rails.application.credentials.dig(:allinpay, :public_key)
|
|
32
|
+
config.inst_no = Rails.application.credentials.dig(:allinpay, :inst_no)
|
|
32
33
|
config.environment = Rails.env.production? ? :production : :test
|
|
33
34
|
config.timeout = 30
|
|
34
35
|
config.logger = Rails.logger
|
|
@@ -44,7 +45,8 @@ AllinpayCnp.configure do |config|
|
|
|
44
45
|
config.merchant_id = '086310030670001'
|
|
45
46
|
config.private_key = File.read('private_key.pem')
|
|
46
47
|
config.public_key = File.read('public_key.pem')
|
|
47
|
-
config.
|
|
48
|
+
config.inst_no = '00000001' # 机构模式下必填,直连商户模式不填
|
|
49
|
+
config.environment = :test # :test 或 :production
|
|
48
50
|
config.timeout = 30
|
|
49
51
|
config.logger = Logger.new($stdout)
|
|
50
52
|
end
|
|
@@ -54,13 +56,24 @@ end
|
|
|
54
56
|
|
|
55
57
|
| 配置项 | 类型 | 必填 | 说明 |
|
|
56
58
|
|--------|------|------|------|
|
|
57
|
-
| `merchant_id` | String |
|
|
58
|
-
| `private_key` | String |
|
|
59
|
-
| `public_key` | String | 否 |
|
|
59
|
+
| `merchant_id` | String | 条件必填 | 商户号。若每次调用时都传入 `merchant_no`,可不在全局配置 |
|
|
60
|
+
| `private_key` | String | 条件必填 | 商户私钥 (PEM 格式),用于请求签名。若每次调用时都传入 `private_key`,可不在全局配置 |
|
|
61
|
+
| `public_key` | String | 否 | 系统公钥 (PEM 格式),用于验证回调/应答签名。亦可在调用时通过 `public_key` / `verify_callback(public_key:)` 传入 |
|
|
62
|
+
| `inst_no` | String | 否 | 机构号,机构模式下必填,直连商户模式留空 |
|
|
60
63
|
| `environment` | Symbol | 否 | `:test` (默认) 或 `:production` |
|
|
61
64
|
| `timeout` | Integer | 否 | 请求超时时间,默认 30 秒 |
|
|
62
65
|
| `logger` | Logger | 否 | 日志对象 |
|
|
63
66
|
|
|
67
|
+
> 多商户模式下可以不在全局配置 `merchant_id` / `private_key`,而是在每次调用时按商户传入。详见 [多商户模式](#多商户模式-per-call-credentials)。
|
|
68
|
+
|
|
69
|
+
### 密钥说明
|
|
70
|
+
|
|
71
|
+
| 密钥 | 用途 | 对应配置 |
|
|
72
|
+
|------|------|----------|
|
|
73
|
+
| 商户私钥 | 请求时计算签名 | `config.private_key` |
|
|
74
|
+
| 系统公钥 | 接收应答/异步通知时验签 | `config.public_key` |
|
|
75
|
+
| 商户公钥 | 上传至通联后台,通联用来验证你的请求签名 | 不需要配置到代码中 |
|
|
76
|
+
|
|
64
77
|
## 使用方法
|
|
65
78
|
|
|
66
79
|
### 获取客户端
|
|
@@ -84,6 +97,9 @@ response = client.unified_pay(
|
|
|
84
97
|
},
|
|
85
98
|
email: 'customer@example.com',
|
|
86
99
|
language: 'zh-hant', # 可选: zh-hant, zh-hans, en
|
|
100
|
+
product_info: [ # 商品信息,可选,会以 JSON 字符串形式发送
|
|
101
|
+
{ sku: 'SKU001', productName: 'Test Product', price: '100.00', quantity: '1' }
|
|
102
|
+
],
|
|
87
103
|
shipping: {
|
|
88
104
|
first_name: 'Peter',
|
|
89
105
|
last_name: 'Zhang',
|
|
@@ -114,7 +130,7 @@ end
|
|
|
114
130
|
### 查询订单 (Query)
|
|
115
131
|
|
|
116
132
|
```ruby
|
|
117
|
-
response = client.query('ORIGINAL_ORDER_123')
|
|
133
|
+
response = client.query(ori_access_order_id: 'ORIGINAL_ORDER_123')
|
|
118
134
|
|
|
119
135
|
if response.success?
|
|
120
136
|
puts "状态: #{response.status}" # SUCCESS, FAIL, PROCESSING
|
|
@@ -168,6 +184,33 @@ class WebhooksController < ApplicationController
|
|
|
168
184
|
end
|
|
169
185
|
```
|
|
170
186
|
|
|
187
|
+
## 多商户模式 (Per-call credentials)
|
|
188
|
+
|
|
189
|
+
如果你的应用需要为多个商户代发请求 (商户模式 / 非机构模式),每个商户拥有独立的密钥对。可在调用时传入 `private_key` / `public_key` / `merchant_no`,会覆盖全局配置:
|
|
190
|
+
|
|
191
|
+
```ruby
|
|
192
|
+
response = client.unified_pay(
|
|
193
|
+
access_order_id: "ORDER_#{Time.now.to_i}",
|
|
194
|
+
amount: '100.00',
|
|
195
|
+
currency: 'HKD',
|
|
196
|
+
urls: { notify_url: '...', return_url: '...' },
|
|
197
|
+
merchant_no: merchant.allinpay_id,
|
|
198
|
+
private_key: merchant.allinpay_private_key,
|
|
199
|
+
public_key: merchant.allinpay_public_key
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# query / refund 同样支持
|
|
203
|
+
client.query(ori_access_order_id: 'ORDER_123',
|
|
204
|
+
merchant_no: merchant.allinpay_id,
|
|
205
|
+
private_key: merchant.allinpay_private_key,
|
|
206
|
+
public_key: merchant.allinpay_public_key)
|
|
207
|
+
|
|
208
|
+
# 回调验签时使用对应商户的 public_key
|
|
209
|
+
client.verify_callback(params, public_key: merchant.allinpay_public_key)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
未传入时会回退到 `AllinpayCnp.configure` 中的全局配置,因此单商户用法完全向后兼容。
|
|
213
|
+
|
|
171
214
|
## Response 对象
|
|
172
215
|
|
|
173
216
|
所有 API 方法都返回 `Response` 对象:
|
|
@@ -216,4 +259,3 @@ lib/
|
|
|
216
259
|
├── response.rb # 响应封装
|
|
217
260
|
└── client.rb # API 客户端
|
|
218
261
|
```
|
|
219
|
-
|
data/lib/allinpay_cnp/client.rb
CHANGED
|
@@ -11,7 +11,9 @@ module AllinpayCnp
|
|
|
11
11
|
amount = opts.fetch(:amount)
|
|
12
12
|
currency = opts.fetch(:currency)
|
|
13
13
|
urls = opts.fetch(:urls)
|
|
14
|
-
|
|
14
|
+
private_key = opts[:private_key]
|
|
15
|
+
public_key = opts[:public_key]
|
|
16
|
+
options = opts.except(:access_order_id, :amount, :currency, :urls, :private_key, :public_key)
|
|
15
17
|
|
|
16
18
|
params = build_unified_pay_params(
|
|
17
19
|
access_order_id: access_order_id,
|
|
@@ -21,7 +23,7 @@ module AllinpayCnp
|
|
|
21
23
|
**options
|
|
22
24
|
)
|
|
23
25
|
|
|
24
|
-
request.post(:unified_pay, params)
|
|
26
|
+
request.post(:unified_pay, params, private_key: private_key, public_key: public_key)
|
|
25
27
|
end
|
|
26
28
|
|
|
27
29
|
def query(hash_or_opts = nil, **kwargs)
|
|
@@ -29,26 +31,33 @@ module AllinpayCnp
|
|
|
29
31
|
|
|
30
32
|
merchant_no = opts.fetch(:merchant_no, config.merchant_id)
|
|
31
33
|
ori_access_order_id = opts.fetch(:ori_access_order_id)
|
|
34
|
+
private_key = opts[:private_key]
|
|
35
|
+
public_key = opts[:public_key]
|
|
32
36
|
|
|
33
37
|
params = {
|
|
34
38
|
version: VERSION,
|
|
39
|
+
instNo: inst_no_param,
|
|
35
40
|
mchtId: merchant_no,
|
|
36
41
|
transType: 'Query',
|
|
42
|
+
accessOrderId: generate_order_id,
|
|
37
43
|
oriAccessOrderId: ori_access_order_id
|
|
38
|
-
}
|
|
39
|
-
request.post(:quickpay, params)
|
|
44
|
+
}.compact
|
|
45
|
+
request.post(:quickpay, params, private_key: private_key, public_key: public_key)
|
|
40
46
|
end
|
|
41
47
|
|
|
42
48
|
def refund(hash_or_opts = nil, **kwargs)
|
|
43
49
|
opts = (hash_or_opts || {}).merge(kwargs).transform_keys(&:to_sym)
|
|
44
50
|
|
|
45
|
-
merchant_no = opts.fetch(:merchant_no)
|
|
51
|
+
merchant_no = opts.fetch(:merchant_no, config.merchant_id)
|
|
46
52
|
ori_access_order_id = opts.fetch(:ori_access_order_id)
|
|
47
53
|
refund_amount = opts.fetch(:refund_amount)
|
|
48
54
|
access_order_id = opts.fetch(:access_order_id, generate_order_id)
|
|
55
|
+
private_key = opts[:private_key]
|
|
56
|
+
public_key = opts[:public_key]
|
|
49
57
|
|
|
50
58
|
params = {
|
|
51
59
|
version: VERSION,
|
|
60
|
+
instNo: inst_no_param,
|
|
52
61
|
mchtId: merchant_no,
|
|
53
62
|
transType: 'Refund',
|
|
54
63
|
accessOrderId: access_order_id,
|
|
@@ -56,19 +65,25 @@ module AllinpayCnp
|
|
|
56
65
|
refundAmount: refund_amount.to_s,
|
|
57
66
|
notifyUrl: opts.fetch(:notify_url, nil)
|
|
58
67
|
}.reject { |_, v| v.nil? }
|
|
59
|
-
request.post(:quickpay, params)
|
|
68
|
+
request.post(:quickpay, params, private_key: private_key, public_key: public_key)
|
|
60
69
|
end
|
|
61
70
|
|
|
62
|
-
def verify_callback(params)
|
|
63
|
-
|
|
71
|
+
def verify_callback(params, public_key: nil)
|
|
72
|
+
key = public_key || config.public_key
|
|
73
|
+
return false unless key
|
|
64
74
|
|
|
65
|
-
Signature.verify(params,
|
|
75
|
+
Signature.verify(params, key)
|
|
66
76
|
end
|
|
67
77
|
|
|
68
78
|
private
|
|
69
79
|
|
|
70
80
|
def generate_order_id
|
|
71
|
-
Time.now.to_i.to_s
|
|
81
|
+
(Time.now.to_f * 1000).to_i.to_s
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def inst_no_param
|
|
85
|
+
val = config.inst_no
|
|
86
|
+
(val.nil? || val.to_s.strip.empty?) ? nil : val.to_s.strip
|
|
72
87
|
end
|
|
73
88
|
|
|
74
89
|
def config
|
|
@@ -80,7 +95,7 @@ module AllinpayCnp
|
|
|
80
95
|
end
|
|
81
96
|
|
|
82
97
|
def build_unified_pay_params(access_order_id:, amount:, currency:, urls:, merchant_no: nil, **options)
|
|
83
|
-
build_base_params(access_order_id, amount, currency, urls, options)
|
|
98
|
+
build_base_params(access_order_id, amount, currency, urls, options, merchant_no)
|
|
84
99
|
.merge(build_shipping_params(options))
|
|
85
100
|
.merge(build_billing_params(options))
|
|
86
101
|
.compact
|
|
@@ -95,11 +110,12 @@ module AllinpayCnp
|
|
|
95
110
|
def build_order_core_params(access_order_id, amount, currency, merchant_no = nil)
|
|
96
111
|
{
|
|
97
112
|
version: VERSION,
|
|
113
|
+
instNo: inst_no_param,
|
|
98
114
|
mchtId: merchant_no || config.merchant_id,
|
|
99
115
|
accessOrderId: access_order_id,
|
|
100
116
|
amount: amount.to_s,
|
|
101
117
|
currency: currency
|
|
102
|
-
}
|
|
118
|
+
}.compact
|
|
103
119
|
end
|
|
104
120
|
|
|
105
121
|
def notify_return_urls(urls)
|
data/lib/allinpay_cnp/request.rb
CHANGED
|
@@ -16,13 +16,13 @@ module AllinpayCnp
|
|
|
16
16
|
}
|
|
17
17
|
}.freeze
|
|
18
18
|
|
|
19
|
-
def post(endpoint_type, params)
|
|
20
|
-
sign_params(params)
|
|
19
|
+
def post(endpoint_type, params, private_key: nil, public_key: nil)
|
|
20
|
+
sign_params(params, private_key)
|
|
21
21
|
url = build_url(endpoint_type)
|
|
22
22
|
log_request(url, params)
|
|
23
23
|
response = send_post(url, params)
|
|
24
24
|
log_response(response)
|
|
25
|
-
Response.new(response, public_key: config.public_key)
|
|
25
|
+
Response.new(response, public_key: public_key || config.public_key)
|
|
26
26
|
rescue Faraday::Error => e
|
|
27
27
|
log_error(e.message)
|
|
28
28
|
Response.new(nil, error: e)
|
|
@@ -34,9 +34,9 @@ module AllinpayCnp
|
|
|
34
34
|
AllinpayCnp.config
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
def sign_params(params)
|
|
37
|
+
def sign_params(params, private_key = nil)
|
|
38
38
|
params[:signType] = 'RSA2'
|
|
39
|
-
params[:sign] = Signature.sign(params, config.private_key)
|
|
39
|
+
params[:sign] = Signature.sign(params, private_key || config.private_key)
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
def build_url(endpoint_type)
|
|
@@ -18,7 +18,7 @@ module AllinpayCnp
|
|
|
18
18
|
|
|
19
19
|
sign_string = build_sign_string(params)
|
|
20
20
|
rsa_verify(sign_string, signature, public_key)
|
|
21
|
-
rescue
|
|
21
|
+
rescue OpenSSL::PKey::PKeyError, OpenSSL::OpenSSLError
|
|
22
22
|
false
|
|
23
23
|
end
|
|
24
24
|
|
|
@@ -49,16 +49,16 @@ module AllinpayCnp
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def load_private_key(key_content)
|
|
52
|
-
if key_content.include?('-----BEGIN')
|
|
52
|
+
if key_content.include?('-----BEGIN PRIVATE KEY') || key_content.include?('-----BEGIN RSA PRIVATE KEY')
|
|
53
53
|
OpenSSL::PKey::RSA.new(key_content)
|
|
54
54
|
else
|
|
55
|
-
pem =
|
|
55
|
+
pem = "-----BEGIN PRIVATE KEY-----\n#{key_content}\n-----END PRIVATE KEY-----"
|
|
56
56
|
OpenSSL::PKey::RSA.new(pem)
|
|
57
57
|
end
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
def load_public_key(key_content)
|
|
61
|
-
if key_content.include?('-----BEGIN')
|
|
61
|
+
if key_content.include?('-----BEGIN PUBLIC KEY')
|
|
62
62
|
OpenSSL::PKey::RSA.new(key_content)
|
|
63
63
|
else
|
|
64
64
|
pem = "-----BEGIN PUBLIC KEY-----\n#{key_content}\n-----END PUBLIC KEY-----"
|
data/lib/allinpay_cnp/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: allinpay_cnp
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- DrinE
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-04-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|