allinpay_cnp 0.1.1

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
+ SHA256:
3
+ metadata.gz: d6a122d0fc477863ecbad367004a05ecae0b0f1876fe04ba82d3c1e24a6bad69
4
+ data.tar.gz: 88c28a107d766171b93f35bd0e78ff33324503f85401191e904899dd72a45d0b
5
+ SHA512:
6
+ metadata.gz: 6cf074a1b6b0de55d3beb38c9624fc3a784bd5896af05b6f82838fc24b4e0914f769815e5bf6e5345c1752cedc5688a4c1490d423ee6c47b2452ad08dcd33c79
7
+ data.tar.gz: adfa675fc31a869b101527c8e54aa3b021ff468f8c33254051fb197a22581f3da027cf6af346353041610e6bc7c2669f6c2532e008df3e71d0c6c4b088021870
data/README.md ADDED
@@ -0,0 +1,219 @@
1
+ # AllinpayCnp
2
+
3
+ 通联支付 CNP 跨境信用卡收单 Ruby SDK。
4
+
5
+ ## 安装
6
+
7
+ ### Gemfile
8
+
9
+ ```ruby
10
+ gem 'allinpay_cnp', git: 'https://github.com/your-org/allinpay_cnp'
11
+ ```
12
+
13
+ ### 本地安装
14
+
15
+ ```bash
16
+ cd allinpay_cnp
17
+ gem build allinpay_cnp.gemspec
18
+ gem install allinpay_cnp-0.1.0.gem
19
+ ```
20
+
21
+ ## 配置
22
+
23
+ ### Rails 项目
24
+
25
+ 创建 `config/initializers/allinpay_cnp.rb`:
26
+
27
+ ```ruby
28
+ AllinpayCnp.configure do |config|
29
+ config.merchant_id = Rails.application.credentials.dig(:allinpay, :merchant_id)
30
+ config.private_key = Rails.application.credentials.dig(:allinpay, :private_key)
31
+ config.public_key = Rails.application.credentials.dig(:allinpay, :public_key)
32
+ config.environment = Rails.env.production? ? :production : :test
33
+ config.timeout = 30
34
+ config.logger = Rails.logger
35
+ end
36
+ ```
37
+
38
+ ### 普通 Ruby 项目
39
+
40
+ ```ruby
41
+ require 'allinpay_cnp'
42
+
43
+ AllinpayCnp.configure do |config|
44
+ config.merchant_id = '086310030670001'
45
+ config.private_key = File.read('private_key.pem')
46
+ config.public_key = File.read('public_key.pem')
47
+ config.environment = :test # :test 或 :production
48
+ config.timeout = 30
49
+ config.logger = Logger.new($stdout)
50
+ end
51
+ ```
52
+
53
+ ### 配置项
54
+
55
+ | 配置项 | 类型 | 必填 | 说明 |
56
+ |--------|------|------|------|
57
+ | `merchant_id` | String | 是 | 商户号 |
58
+ | `private_key` | String | 是 | 商户私钥 (PEM 格式) |
59
+ | `public_key` | String | 否 | CNP 公钥,用于验证回调签名 |
60
+ | `environment` | Symbol | 否 | `:test` (默认) 或 `:production` |
61
+ | `timeout` | Integer | 否 | 请求超时时间,默认 30 秒 |
62
+ | `logger` | Logger | 否 | 日志对象 |
63
+
64
+ ## 使用方法
65
+
66
+ ### 获取客户端
67
+
68
+ ```ruby
69
+ client = AllinpayCnp.client
70
+ ```
71
+
72
+ ### 统一收银台支付 (Unified Pay)
73
+
74
+ 跳转到通联支付页面完成支付:
75
+
76
+ ```ruby
77
+ response = client.unified_pay(
78
+ access_order_id: "ORDER_#{Time.now.to_i}",
79
+ amount: '100.00',
80
+ currency: 'HKD',
81
+ urls: {
82
+ notify_url: 'https://your-domain.com/webhooks/allinpay',
83
+ return_url: 'https://your-domain.com/payments/complete'
84
+ },
85
+ email: 'customer@example.com',
86
+ language: 'zh-hant', # 可选: zh-hant, zh-hans, en
87
+ shipping: {
88
+ first_name: 'Peter',
89
+ last_name: 'Zhang',
90
+ address1: '123 Test Street',
91
+ city: 'Hong Kong',
92
+ country: 'HK',
93
+ zip_code: '000000',
94
+ phone: '12345678'
95
+ },
96
+ billing: {
97
+ first_name: 'Peter',
98
+ last_name: 'Zhang',
99
+ address1: '123 Test Street',
100
+ city: 'Hong Kong',
101
+ country: 'HK',
102
+ zip_code: '000000',
103
+ phone: '12345678'
104
+ }
105
+ )
106
+
107
+ if response.success?
108
+ redirect_to response.payment_url
109
+ else
110
+ puts "Error: #{response.result_desc}"
111
+ end
112
+ ```
113
+
114
+ ### 查询订单 (Query)
115
+
116
+ ```ruby
117
+ response = client.query('ORIGINAL_ORDER_123')
118
+
119
+ if response.success?
120
+ puts "状态: #{response.status}" # SUCCESS, FAIL, PROCESSING
121
+ puts "金额: #{response.amount}"
122
+ puts "币种: #{response.currency}"
123
+ puts "卡号: #{response.card_no}" # 脱敏卡号
124
+ puts "卡组织: #{response.card_orgn}" # VISA, MASTERCARD 等
125
+ end
126
+ ```
127
+
128
+ ### 退款 (Refund)
129
+
130
+ ```ruby
131
+ response = client.refund(
132
+ ori_access_order_id: 'ORIGINAL_ORDER_123',
133
+ refund_amount: '50.00'
134
+ )
135
+
136
+ if response.success?
137
+ puts '退款成功'
138
+ else
139
+ puts "退款失败: #{response.result_desc}"
140
+ end
141
+ ```
142
+
143
+ ### 验证回调签名
144
+
145
+ ```ruby
146
+ # Rails Controller
147
+ class WebhooksController < ApplicationController
148
+ skip_before_action :verify_authenticity_token, only: [:allinpay]
149
+
150
+ def allinpay
151
+ client = AllinpayCnp.client
152
+
153
+ unless client.verify_callback(params.to_unsafe_h)
154
+ render plain: 'FAIL' and return
155
+ end
156
+
157
+ if params[:resultCode] == '0000'
158
+ payment = Payment.find_by(gateway_order_id: params[:accessOrderId])
159
+ payment&.mark_as_paid!(
160
+ transaction_ref: params[:orderId],
161
+ card_no: params[:cardNo],
162
+ card_orgn: params[:cardOrgn]
163
+ )
164
+ end
165
+
166
+ render plain: 'SUCCESS' # 必须返回 SUCCESS
167
+ end
168
+ end
169
+ ```
170
+
171
+ ## Response 对象
172
+
173
+ 所有 API 方法都返回 `Response` 对象:
174
+
175
+ ```ruby
176
+ response.success? # 是否成功 (resultCode == '0000')
177
+ response.failure? # 是否失败
178
+ response.result_code # 返回码
179
+ response.result_desc # 返回描述
180
+ response.payment_url # 支付页面 URL (unified_pay)
181
+ response.access_order_id # 商户订单号
182
+ response.order_id # CNP 系统订单号
183
+ response.status # 订单状态 (query)
184
+ response.amount # 金额
185
+ response.currency # 币种
186
+ response.card_no # 脱敏卡号
187
+ response.card_orgn # 卡组织
188
+ response.body # 原始响应 Hash
189
+ response['customField'] # 获取任意字段
190
+ ```
191
+
192
+ ## API 地址
193
+
194
+ | 环境 | Unified Pay | QuickPay |
195
+ |------|-------------|----------|
196
+ | 测试 | https://cnp-test.allinpay.com/gateway/cnp/unifiedPay | https://cnp-test.allinpay.com/gateway/cnp/quickpay |
197
+ | 生产 | https://cnp.allinpay.com/gateway/cnp/unifiedPay | https://cnp.allinpay.com/gateway/cnp/quickpay |
198
+
199
+ ## 运行测试
200
+
201
+ ```bash
202
+ bundle install
203
+ bundle exec rspec
204
+ ```
205
+
206
+ ## 目录结构
207
+
208
+ ```
209
+ lib/
210
+ ├── allinpay_cnp.rb # 主入口
211
+ └── allinpay_cnp/
212
+ ├── version.rb # 版本号
213
+ ├── config.rb # 配置类
214
+ ├── signature.rb # RSA2 签名
215
+ ├── request.rb # HTTP 请求
216
+ ├── response.rb # 响应封装
217
+ └── client.rb # API 客户端
218
+ ```
219
+
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AllinpayCnp
4
+ class Client
5
+ VERSION = 'V2.0.0'
6
+
7
+ def unified_pay(access_order_id:, amount:, currency:, urls:, **options)
8
+ params = build_unified_pay_params(
9
+ access_order_id: access_order_id,
10
+ amount: amount,
11
+ currency: currency,
12
+ urls: urls,
13
+ **options
14
+ )
15
+ request.post(:unified_pay, params)
16
+ end
17
+
18
+ def query(ori_access_order_id)
19
+ params = {
20
+ version: VERSION,
21
+ mchtId: config.merchant_id,
22
+ transType: 'Query',
23
+ oriAccessOrderId: ori_access_order_id
24
+ }
25
+ request.post(:quickpay, params)
26
+ end
27
+
28
+ def refund(ori_access_order_id:, refund_amount:, access_order_id: nil)
29
+ params = {
30
+ version: VERSION,
31
+ mchtId: config.merchant_id,
32
+ transType: 'Refund',
33
+ accessOrderId: access_order_id,
34
+ oriAccessOrderId: ori_access_order_id,
35
+ refundAmount: refund_amount.to_s
36
+ }
37
+ request.post(:quickpay, params)
38
+ end
39
+
40
+ def verify_callback(params)
41
+ return false unless config.public_key
42
+
43
+ Signature.verify(params, config.public_key)
44
+ end
45
+
46
+ private
47
+
48
+ def config
49
+ AllinpayCnp.config
50
+ end
51
+
52
+ def request
53
+ @request ||= Request.new
54
+ end
55
+
56
+ def build_unified_pay_params(access_order_id:, amount:, currency:, urls:, **options)
57
+ build_base_params(access_order_id, amount, currency, urls, options)
58
+ .merge(build_shipping_params(options))
59
+ .merge(build_billing_params(options))
60
+ .compact
61
+ end
62
+
63
+ def build_base_params(access_order_id, amount, currency, urls, options)
64
+ build_order_core_params(access_order_id, amount, currency)
65
+ .merge(notify_return_urls(urls))
66
+ .merge(build_base_option_params(options))
67
+ end
68
+
69
+ def build_order_core_params(access_order_id, amount, currency)
70
+ {
71
+ version: VERSION,
72
+ mchtId: config.merchant_id,
73
+ accessOrderId: access_order_id,
74
+ amount: amount.to_s,
75
+ currency: currency
76
+ }
77
+ end
78
+
79
+ def notify_return_urls(urls)
80
+ { notifyUrl: urls[:notify_url], returnUrl: urls[:return_url] }
81
+ end
82
+
83
+ def build_base_option_params(options)
84
+ {
85
+ language: options[:language] || 'zh-hant',
86
+ email: options[:email],
87
+ productInfo: options[:product_info]&.to_json
88
+ }
89
+ end
90
+
91
+ def build_shipping_params(options)
92
+ {
93
+ shippingFirstName: options.dig(:shipping, :first_name),
94
+ shippingLastName: options.dig(:shipping, :last_name),
95
+ shippingAddress1: options.dig(:shipping, :address1),
96
+ shippingAddress2: options.dig(:shipping, :address2),
97
+ shippingCity: options.dig(:shipping, :city),
98
+ shippingState: options.dig(:shipping, :state),
99
+ shippingCountry: options.dig(:shipping, :country),
100
+ shippingZipCode: options.dig(:shipping, :zip_code),
101
+ shippingPhone: options.dig(:shipping, :phone)
102
+ }
103
+ end
104
+
105
+ def build_billing_params(options)
106
+ {
107
+ billingFirstName: options.dig(:billing, :first_name),
108
+ billingLastName: options.dig(:billing, :last_name),
109
+ billingAddress1: options.dig(:billing, :address1),
110
+ billingAddress2: options.dig(:billing, :address2),
111
+ billingCity: options.dig(:billing, :city),
112
+ billingState: options.dig(:billing, :state),
113
+ billingCountry: options.dig(:billing, :country),
114
+ billingZipCode: options.dig(:billing, :zip_code),
115
+ billingPhone: options.dig(:billing, :phone)
116
+ }
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AllinpayCnp
4
+ class Config
5
+ ENVIRONMENTS = %i[test production].freeze
6
+
7
+ attr_accessor :merchant_id, :private_key, :public_key, :logger, :timeout
8
+ attr_reader :environment
9
+
10
+ def initialize
11
+ @environment = :test
12
+ @timeout = 30
13
+ end
14
+
15
+ def environment=(value)
16
+ value = value.to_sym
17
+ raise ArgumentError, "Invalid environment: #{value}" unless ENVIRONMENTS.include?(value)
18
+
19
+ @environment = value
20
+ end
21
+
22
+ def test?
23
+ @environment == :test
24
+ end
25
+
26
+ def production?
27
+ @environment == :production
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'uri'
5
+
6
+ module AllinpayCnp
7
+ class Request
8
+ ENDPOINTS = {
9
+ test: {
10
+ quickpay: 'https://cnp-test.allinpay.com/gateway/cnp/quickpay',
11
+ unified_pay: 'https://cnp-test.allinpay.com/gateway/cnp/unifiedPay'
12
+ },
13
+ production: {
14
+ quickpay: 'https://cnp.allinpay.com/gateway/cnp/quickpay',
15
+ unified_pay: 'https://cnp.allinpay.com/gateway/cnp/unifiedPay'
16
+ }
17
+ }.freeze
18
+
19
+ def post(endpoint_type, params)
20
+ sign_params(params)
21
+ url = build_url(endpoint_type)
22
+ log_request(url, params)
23
+ response = send_post(url, params)
24
+ log_response(response)
25
+ Response.new(response, public_key: config.public_key)
26
+ rescue Faraday::Error => e
27
+ log_error(e.message)
28
+ Response.new(nil, error: e)
29
+ end
30
+
31
+ private
32
+
33
+ def config
34
+ AllinpayCnp.config
35
+ end
36
+
37
+ def sign_params(params)
38
+ params[:signType] = 'RSA2'
39
+ params[:sign] = Signature.sign(params, config.private_key)
40
+ end
41
+
42
+ def build_url(endpoint_type)
43
+ ENDPOINTS[config.environment][endpoint_type]
44
+ end
45
+
46
+ def send_post(url, params)
47
+ connection.post(url) do |req|
48
+ req.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
49
+ req.body = encode_params(params)
50
+ end
51
+ end
52
+
53
+ def encode_params(params)
54
+ params
55
+ .reject { |_, v| v.nil? || v.to_s.empty? }
56
+ .map { |k, v| "#{k}=#{URI.encode_www_form_component(v.to_s)}" }
57
+ .join('&')
58
+ end
59
+
60
+ def connection
61
+ @connection ||= Faraday.new do |conn|
62
+ conn.options.timeout = config.timeout
63
+ conn.options.open_timeout = config.timeout
64
+ conn.adapter Faraday.default_adapter
65
+ end
66
+ end
67
+
68
+ def log_request(url, params)
69
+ return unless config.logger
70
+
71
+ config.logger.info("[AllinpayCnp] POST #{url}")
72
+ config.logger.debug("[AllinpayCnp] Params: #{params.reject { |k, _| k == :sign }}")
73
+ end
74
+
75
+ def log_response(response)
76
+ return unless config.logger
77
+
78
+ config.logger.info("[AllinpayCnp] Response: #{response.status}")
79
+ config.logger.debug("[AllinpayCnp] Body: #{response.body}")
80
+ end
81
+
82
+ def log_error(message)
83
+ return unless config.logger
84
+
85
+ config.logger.error("[AllinpayCnp] Error: #{message}")
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module AllinpayCnp
6
+ class Response
7
+ SUCCESS_CODE = '0000'
8
+
9
+ def initialize(http_response, error: nil, public_key: nil)
10
+ @http_response = http_response
11
+ @error = error
12
+ @public_key = public_key
13
+ @body = nil
14
+ end
15
+
16
+ def http_success?
17
+ return false if @error
18
+
19
+ @http_response&.success?
20
+ end
21
+
22
+ def success?
23
+ http_success? && result_code == SUCCESS_CODE
24
+ end
25
+
26
+ def failure?
27
+ !success?
28
+ end
29
+
30
+ def body
31
+ @body ||= parse_body
32
+ end
33
+
34
+ def raw_body
35
+ @http_response&.body
36
+ end
37
+
38
+ def result_code
39
+ body['resultCode']
40
+ end
41
+
42
+ def result_desc
43
+ body['resultDesc']
44
+ end
45
+
46
+ def access_order_id
47
+ body['accessOrderId']
48
+ end
49
+
50
+ def order_id
51
+ body['orderId']
52
+ end
53
+
54
+ def payment_url
55
+ body['paymentUrl'] || body['counterUrl'] || body['url']
56
+ end
57
+
58
+ def status
59
+ body['status']
60
+ end
61
+
62
+ def status_desc
63
+ body['statusDesc']
64
+ end
65
+
66
+ def amount
67
+ body['amount']
68
+ end
69
+
70
+ def currency
71
+ body['currency']
72
+ end
73
+
74
+ def refund_amount
75
+ body['refundAmount']
76
+ end
77
+
78
+ def trans_time
79
+ time_str = body['transTime']
80
+ return nil unless time_str
81
+
82
+ Time.strptime(time_str, '%Y%m%d%H%M%S')
83
+ rescue ArgumentError
84
+ nil
85
+ end
86
+
87
+ def card_no
88
+ body['cardNo']
89
+ end
90
+
91
+ def card_orgn
92
+ body['cardOrgn']
93
+ end
94
+
95
+ def sign
96
+ body['sign']
97
+ end
98
+
99
+ def valid_signature?
100
+ return false unless @public_key && sign
101
+
102
+ Signature.verify(body, @public_key)
103
+ end
104
+
105
+ def [](key)
106
+ body[key.to_s]
107
+ end
108
+
109
+ def to_h
110
+ {
111
+ success: success?,
112
+ result_code: result_code,
113
+ result_desc: result_desc,
114
+ access_order_id: access_order_id,
115
+ order_id: order_id,
116
+ payment_url: payment_url,
117
+ status: status,
118
+ amount: amount,
119
+ currency: currency,
120
+ raw: body
121
+ }
122
+ end
123
+
124
+ private
125
+
126
+ attr_reader :http_response, :error, :public_key
127
+
128
+ def parse_body
129
+ return { 'resultCode' => 'NETWORK_ERROR', 'resultDesc' => @error.message } if @error
130
+ return {} if @http_response.nil?
131
+
132
+ JSON.parse(@http_response.body)
133
+ rescue JSON::ParserError
134
+ { 'resultCode' => 'PARSE_ERROR', 'resultDesc' => 'Invalid JSON', 'raw' => @http_response.body }
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+ require 'base64'
5
+
6
+ module AllinpayCnp
7
+ module Signature
8
+ class << self
9
+ def sign(params, private_key)
10
+ sign_string = build_sign_string(params)
11
+ rsa_sign(sign_string, private_key)
12
+ end
13
+
14
+ def verify(params, public_key)
15
+ params = params.transform_keys(&:to_s)
16
+ signature = params.delete('sign')
17
+ return false if signature.nil? || signature.empty?
18
+
19
+ sign_string = build_sign_string(params)
20
+ rsa_verify(sign_string, signature, public_key)
21
+ rescue StandardError
22
+ false
23
+ end
24
+
25
+ def build_sign_string(params)
26
+ params
27
+ .transform_keys(&:to_s)
28
+ .reject { |k, v| k == 'sign' || v.nil? || v.to_s.strip.empty? }
29
+ .sort_by { |k, _| k }
30
+ .map { |k, v| "#{k}=#{v.to_s.strip}" }
31
+ .join('&')
32
+ end
33
+
34
+ private
35
+
36
+ def rsa_sign(content, private_key_pem)
37
+ pkey = load_private_key(private_key_pem)
38
+ signature = pkey.sign(OpenSSL::Digest.new('SHA256'), content)
39
+ Base64.strict_encode64(signature)
40
+ end
41
+
42
+ def rsa_verify(content, signature, public_key_pem)
43
+ pkey = load_public_key(public_key_pem)
44
+ pkey.verify(
45
+ OpenSSL::Digest.new('SHA256'),
46
+ Base64.decode64(signature),
47
+ content
48
+ )
49
+ end
50
+
51
+ def load_private_key(key_content)
52
+ if key_content.include?('-----BEGIN')
53
+ OpenSSL::PKey::RSA.new(key_content)
54
+ else
55
+ pem = '-----BEGIN PRIVATE KEY-----\n#{key_content}\n-----END PRIVATE KEY-----'
56
+ OpenSSL::PKey::RSA.new(pem)
57
+ end
58
+ end
59
+
60
+ def load_public_key(key_content)
61
+ if key_content.include?('-----BEGIN')
62
+ OpenSSL::PKey::RSA.new(key_content)
63
+ else
64
+ pem = "-----BEGIN PUBLIC KEY-----\n#{key_content}\n-----END PUBLIC KEY-----"
65
+ OpenSSL::PKey::RSA.new(pem)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AllinpayCnp
4
+ VERSION = '0.1.1'
5
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'allinpay_cnp/version'
4
+ require_relative 'allinpay_cnp/config'
5
+ require_relative 'allinpay_cnp/signature'
6
+ require_relative 'allinpay_cnp/response'
7
+ require_relative 'allinpay_cnp/request'
8
+ require_relative 'allinpay_cnp/client'
9
+
10
+ module AllinpayCnp
11
+ class Error < StandardError; end
12
+
13
+ class << self
14
+ def config
15
+ @config ||= Config.new
16
+ end
17
+
18
+ def configure
19
+ if block_given?
20
+ yield(config)
21
+ end
22
+ config
23
+ end
24
+
25
+ def client
26
+ Client.new
27
+ end
28
+ end
29
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: allinpay_cnp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - DrinE
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-02-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '3.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '1.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '3.0'
33
+ description: 通联支付 CNP 跨境信用卡收单 Ruby SDK
34
+ email:
35
+ - drine.liu@gmail.com
36
+ executables: []
37
+ extensions: []
38
+ extra_rdoc_files: []
39
+ files:
40
+ - README.md
41
+ - lib/allinpay_cnp.rb
42
+ - lib/allinpay_cnp/client.rb
43
+ - lib/allinpay_cnp/config.rb
44
+ - lib/allinpay_cnp/request.rb
45
+ - lib/allinpay_cnp/response.rb
46
+ - lib/allinpay_cnp/signature.rb
47
+ - lib/allinpay_cnp/version.rb
48
+ homepage: https://github.com/CoolDrinELiu/allinpay
49
+ licenses:
50
+ - MIT
51
+ metadata: {}
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 2.7.0
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubygems_version: 3.5.9
68
+ signing_key:
69
+ specification_version: 4
70
+ summary: Ruby SDK for Allinpay CNP cross-border payment gateway
71
+ test_files: []