spgateway_client 1.0.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 +7 -0
- data/.gitignore +10 -0
- data/.rubocop.yml +6 -0
- data/.travis.yml +10 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +162 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/examples/public/style.css +15 -0
- data/examples/server.rb +47 -0
- data/examples/views/mpg.erb +33 -0
- data/examples/views/period.erb +33 -0
- data/lib/spgateway.rb +6 -0
- data/lib/spgateway/client.rb +246 -0
- data/lib/spgateway/core_ext/hash.rb +10 -0
- data/lib/spgateway/errors.rb +9 -0
- data/lib/spgateway/version.rb +4 -0
- data/lib/spgateway_client.rb +2 -0
- data/spgateway.gemspec +28 -0
- metadata +136 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 69f2453db8b5fea37509bba4fb769dbb47341555
|
4
|
+
data.tar.gz: 3b6416530d4e1727a97267c14551a3023dd99ec0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f1815e70ee2b30ff151a84ea9fbf5fb031ced4c0c632218af770d6ed2b95e2bda647dc1387350a5524813b3961e5fa6c19561d9b9d86c8b4bdd2b1e785f6937c
|
7
|
+
data.tar.gz: 5f31b1b71728080bcd2fd346377970093611f038d331492ee81393f4db1e7d2cec3d0791c8bef9a09831ca41a954cd16283d87d0611fc23f48cc3851f16185a1
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Calvert
|
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,162 @@
|
|
1
|
+
[](https://badge.fury.io/rb/spgateway_client)
|
2
|
+
[](https://travis-ci.org/CalvertYang/spgateway)
|
3
|
+
|
4
|
+
# 智付通 Spgateway
|
5
|
+
|
6
|
+
這是智付通 API 的 Ruby 包裝,更多資訊請參閱 [API 文件專區](https://www.spgateway.com/info/site_description/api_description)。
|
7
|
+
|
8
|
+
- 這不是 Rails 插件,只是個 API 包裝。
|
9
|
+
- 使用時只需要傳送需要的參數即可,不用產生檢查碼,`spgateway_client` 會自己產生。
|
10
|
+
- 感謝[大兜](https://github.com/tonytonyjan)撰寫的 [allpay](https://github.com/tonytonyjan/allpay)
|
11
|
+
|
12
|
+
## 安裝
|
13
|
+
|
14
|
+
```bash
|
15
|
+
gem install spgateway_client
|
16
|
+
```
|
17
|
+
|
18
|
+
## 使用
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
test_client = Spgateway::Client.new({
|
22
|
+
merchant_id: 'MERCHANT_ID',
|
23
|
+
hash_key: 'HASH_KEY',
|
24
|
+
hash_iv: 'HASH_IV',
|
25
|
+
mode: :test
|
26
|
+
})
|
27
|
+
|
28
|
+
production_client = Spgateway::Client.new({
|
29
|
+
merchant_id: 'MERCHANT_ID',
|
30
|
+
hash_key: 'HASH_KEY',
|
31
|
+
hash_iv: 'HASH_IV'
|
32
|
+
})
|
33
|
+
|
34
|
+
test_client.query_trade_info({
|
35
|
+
MerchantOrderNo: '4e19cab1',
|
36
|
+
Amt: 100
|
37
|
+
})
|
38
|
+
```
|
39
|
+
|
40
|
+
本文件撰寫時,智付通共有 5 個 API:
|
41
|
+
|
42
|
+
API 名稱 | 版本 | 說明
|
43
|
+
--- | --- | ---
|
44
|
+
MPG API | 1.0.3 | MPG(Multi Payment Gateway),單一串接多種支付方式。<br>透過 MPG API 可串接平台提供的所有支付方式。
|
45
|
+
交易查詢 API | 1.0.1 | 透過交易查詢 API 可自動化查核所有交易是否同步更新付款資訊。
|
46
|
+
信用卡-取消授權 API | 1.0.0 | 透過取消授權 API,可進行信用卡交易授權取消,以返還持卡人及商店信用卡額度。
|
47
|
+
信用卡-請退款 API | 1.0.0 | 透過請退款 API,可進行信用卡交易的請退款。
|
48
|
+
信用卡-定期定額 API | 1.0.6 | 透過信用卡定期定額 API,可進行信用卡定期定額交易。
|
49
|
+
|
50
|
+
詳細 API 參數請參閱智付通技術串接手冊,注意幾點:
|
51
|
+
|
52
|
+
- 使用時不用煩惱 `MerchantID`、`RespondType`、`CheckValue`、`TimeStamp` 及 `Version`,正如上述範例一樣。
|
53
|
+
- MPG/信用卡-定期定額 API 回傳的內容是 HTML,這個請求應該是交給瀏覽器發送的。
|
54
|
+
|
55
|
+
## Spgateway::Client
|
56
|
+
|
57
|
+
實體方法 | 回傳 | 說明
|
58
|
+
--- | --- | ---
|
59
|
+
`verify_check_code(params)` | `Boolean` | 用於檢查收到的參數,其檢查碼是否正確,用於智付通的 `NotifyURL` 參數及檢核資料回傳的合法性。
|
60
|
+
`generate_mpg_params(params)` | `Hash` | 用於產生 MPG API 表單需要的參數。
|
61
|
+
`query_trade_info(params)` | `Hash` | 用於查詢交易狀態。
|
62
|
+
`credit_card_deauthorize(params)` | `Hash` | 用於進行信用卡交易授權取消作業。
|
63
|
+
`credit_card_deauthorize_by_merchant_order_no(params)` | `Hash` | `credit_card_deauthorize` 的捷徑方法,將 `IndexType` 設為 1
|
64
|
+
`credit_card_deauthorize_by_trade_no(params)` | `Hash` | `credit_card_deauthorize` 的捷徑方法,將 `IndexType` 設為 2
|
65
|
+
`credit_card_collect_refund(params)` | `Hash` | 用於進行信用卡交易的請退款作業。
|
66
|
+
`credit_card_collect_refund_by_merchant_order_no(params)` | `Hash` | `credit_card_collect_refund` 的捷徑方法,將 `IndexType` 設為 1
|
67
|
+
`credit_card_collect_refund_by_trade_no(params)` | `Hash` | `credit_card_collect_refund` 的捷徑方法,將 `IndexType` 設為 2
|
68
|
+
`generate_credit_card_period_params(params)` | `Hash` | 用於產生信用卡-定期定額 API 表單需要的參數。
|
69
|
+
|
70
|
+
## 使用範例
|
71
|
+
|
72
|
+
##### MPG、信用卡-定期定額
|
73
|
+
```bash
|
74
|
+
git clone git@github.com:CalvertYang/spgateway.git
|
75
|
+
cd spgateway
|
76
|
+
bundle install
|
77
|
+
ruby examples/server.rb
|
78
|
+
```
|
79
|
+
|
80
|
+
- MPG(GET):http://localhost:4567/mpg
|
81
|
+
- 信用卡-定期定額(GET):http://localhost:4567/period
|
82
|
+
- 驗證資料合法性(POST):http://localhost:4567/validate
|
83
|
+
|
84
|
+
##### 交易狀態查詢
|
85
|
+
```ruby
|
86
|
+
result = test_client.query_trade_info({
|
87
|
+
MerchantOrderNo: '4e19cab1',
|
88
|
+
Amt: 100
|
89
|
+
})
|
90
|
+
|
91
|
+
puts "Result: #{result}"
|
92
|
+
puts "Valid?: #{@client.verify_check_code(result)}"
|
93
|
+
```
|
94
|
+
|
95
|
+
##### 信用卡-取消授權模組
|
96
|
+
```ruby
|
97
|
+
result = test_client.credit_card_deauthorize({
|
98
|
+
TradeNo: '16010112345678901',
|
99
|
+
Amt: 100,
|
100
|
+
IndexType: 1
|
101
|
+
})
|
102
|
+
|
103
|
+
puts "Result: #{result}"
|
104
|
+
puts "Valid?: #{@client.verify_check_code(result)}"
|
105
|
+
```
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
result = test_client.credit_card_deauthorize_by_merchant_order_no({
|
109
|
+
MerchantOrderNo: '4e19cab1',
|
110
|
+
Amt: 100
|
111
|
+
})
|
112
|
+
|
113
|
+
puts "Result: #{result}"
|
114
|
+
puts "Valid?: #{@client.verify_check_code(result)}"
|
115
|
+
```
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
result = test_client.credit_card_deauthorize_by_trade_no({
|
119
|
+
TradeNo: '16010112345678901',
|
120
|
+
Amt: 100
|
121
|
+
})
|
122
|
+
|
123
|
+
puts "Result: #{result}"
|
124
|
+
puts "Valid?: #{@client.verify_check_code(result)}"
|
125
|
+
```
|
126
|
+
|
127
|
+
##### 信用卡-請退款模組
|
128
|
+
```ruby
|
129
|
+
result = test_client.credit_card_collect_refund({
|
130
|
+
MerchantOrderNo: '4e19cab1',
|
131
|
+
Amt: 100,
|
132
|
+
CloseType: 2,
|
133
|
+
IndexType: 1
|
134
|
+
})
|
135
|
+
|
136
|
+
puts "Result: #{result}"
|
137
|
+
```
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
result = test_client.credit_card_collect_refund_by_merchant_order_no({
|
141
|
+
MerchantOrderNo: '4e19cab1',
|
142
|
+
Amt: 100,
|
143
|
+
CloseType: 2
|
144
|
+
})
|
145
|
+
|
146
|
+
puts "Result: #{result}"
|
147
|
+
```
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
result = test_client.credit_card_collect_refund_by_trade_no({
|
151
|
+
TradeNo: '16010112345678901',
|
152
|
+
Amt: 100,
|
153
|
+
CloseType: 2
|
154
|
+
})
|
155
|
+
|
156
|
+
puts "Result: #{result}"
|
157
|
+
```
|
158
|
+
|
159
|
+
## License
|
160
|
+
MIT
|
161
|
+
|
162
|
+

|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'spgateway_client'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require 'irb'
|
15
|
+
IRB.start
|
data/bin/setup
ADDED
data/examples/server.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
$LOAD_PATH << File.expand_path('../../lib', __FILE__)
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'sinatra'
|
5
|
+
require 'spgateway'
|
6
|
+
|
7
|
+
client = Spgateway::Client.new(
|
8
|
+
merchant_id: '123456',
|
9
|
+
hash_key: '12345678901234567890123456789012',
|
10
|
+
hash_iv: '1234567890123456',
|
11
|
+
mode: :test
|
12
|
+
)
|
13
|
+
|
14
|
+
get '/mpg' do
|
15
|
+
@client_options = client.options
|
16
|
+
@params = client.generate_mpg_params(
|
17
|
+
MerchantOrderNo: SecureRandom.hex(4),
|
18
|
+
Amt: 200,
|
19
|
+
ItemDesc: '一般交易測試',
|
20
|
+
Email: 'hello@localhost.com',
|
21
|
+
EmailModify: 0,
|
22
|
+
LoginType: 0,
|
23
|
+
CREDIT: 1
|
24
|
+
)
|
25
|
+
|
26
|
+
erb :mpg
|
27
|
+
end
|
28
|
+
|
29
|
+
get '/period' do
|
30
|
+
@client_options = client.options
|
31
|
+
@params = client.generate_credit_card_period_params(
|
32
|
+
MerchantOrderNo: SecureRandom.hex(4),
|
33
|
+
ProdDesc: '定期定額交易測試',
|
34
|
+
PeriodAmt: 100,
|
35
|
+
PeriodAmtMode: 'Total',
|
36
|
+
PeriodType: 'M',
|
37
|
+
PeriodPoint: '01',
|
38
|
+
PeriodStartType: 1,
|
39
|
+
PeriodTimes: '5'
|
40
|
+
)
|
41
|
+
|
42
|
+
erb :period
|
43
|
+
end
|
44
|
+
|
45
|
+
post '/validate' do
|
46
|
+
client.verify_check_code(request.POST).to_s
|
47
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<title>MPG API</title>
|
6
|
+
<link rel="stylesheet" type="text/css" href="<%= url("/style.css")%>">
|
7
|
+
</head>
|
8
|
+
<body>
|
9
|
+
<form action="https://ccore.spgateway.com/MPG/mpg_gateway" method="post">
|
10
|
+
<table>
|
11
|
+
<caption>MPG API</caption>
|
12
|
+
<% @client_options.each do |k, v| %>
|
13
|
+
<tr>
|
14
|
+
<th class="title"><%= k %></th>
|
15
|
+
<td><%= v %></td>
|
16
|
+
</tr>
|
17
|
+
<% end %>
|
18
|
+
<% @params.each do |k, v| %>
|
19
|
+
<tr>
|
20
|
+
<th class="title"><%= k %></th>
|
21
|
+
<td>
|
22
|
+
<%= v %>
|
23
|
+
<input type="hidden" name="<%= k %>" value="<%= v %>">
|
24
|
+
</td>
|
25
|
+
</tr>
|
26
|
+
<% end %>
|
27
|
+
<tr>
|
28
|
+
<td colspan="2"><input type="submit"></td>
|
29
|
+
</tr>
|
30
|
+
</table>
|
31
|
+
</form>
|
32
|
+
</body>
|
33
|
+
</html>
|
@@ -0,0 +1,33 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<title>信用卡-定期定額 API</title>
|
6
|
+
<link rel="stylesheet" type="text/css" href="<%= url("/style.css")%>">
|
7
|
+
</head>
|
8
|
+
<body>
|
9
|
+
<form action="https://ccore.spgateway.com/MPG/period" method="post">
|
10
|
+
<table>
|
11
|
+
<caption>信用卡-定期定額 API</caption>
|
12
|
+
<% @client_options.each do |k, v| %>
|
13
|
+
<tr>
|
14
|
+
<th class="title"><%= k %></th>
|
15
|
+
<td><%= v %></td>
|
16
|
+
</tr>
|
17
|
+
<% end %>
|
18
|
+
<% @params.each do |k, v| %>
|
19
|
+
<tr>
|
20
|
+
<th class="title"><%= k %></th>
|
21
|
+
<td>
|
22
|
+
<%= v %>
|
23
|
+
<input type="hidden" name="<%= k %>" value="<%= v %>">
|
24
|
+
</td>
|
25
|
+
</tr>
|
26
|
+
<% end %>
|
27
|
+
<tr>
|
28
|
+
<td colspan="2"><input type="submit"></td>
|
29
|
+
</tr>
|
30
|
+
</table>
|
31
|
+
</form>
|
32
|
+
</body>
|
33
|
+
</html>
|
data/lib/spgateway.rb
ADDED
@@ -0,0 +1,246 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'net/http'
|
3
|
+
require 'cgi'
|
4
|
+
require 'digest'
|
5
|
+
require 'spgateway/errors'
|
6
|
+
require 'spgateway/core_ext/hash'
|
7
|
+
|
8
|
+
module Spgateway
|
9
|
+
class Client # :nodoc:
|
10
|
+
TRANSACTION_API_ENDPOINTS = {
|
11
|
+
test: 'https://ccore.spgateway.com/API/QueryTradeInfo',
|
12
|
+
production: 'https://core.spgateway.com/API/QueryTradeInfo'
|
13
|
+
}.freeze
|
14
|
+
CREDITCARD_COLLECT_REFUND_API_ENDPOINTS = {
|
15
|
+
test: 'https://ccore.spgateway.com/API/CreditCard/Close',
|
16
|
+
production: 'https://core.spgateway.com/API/CreditCard/Close'
|
17
|
+
}.freeze
|
18
|
+
CREDITCARD_DEAUTHORIZE_API_ENDPOINTS = {
|
19
|
+
test: 'https://ccore.spgateway.com/API/CreditCard/Cancel',
|
20
|
+
production: 'https://core.spgateway.com/API/CreditCard/Cancel'
|
21
|
+
}.freeze
|
22
|
+
NEED_CHECK_VALUE_APIS = [
|
23
|
+
:query_trade_info # Transaction API
|
24
|
+
].freeze
|
25
|
+
|
26
|
+
attr_reader :options
|
27
|
+
|
28
|
+
def initialize(options = {})
|
29
|
+
@options = { mode: :production }.merge!(options)
|
30
|
+
|
31
|
+
case @options[:mode]
|
32
|
+
when :test, :production
|
33
|
+
option_required! :merchant_id, :hash_key, :hash_iv
|
34
|
+
else
|
35
|
+
raise InvalidMode, %(option :mode is either :test or :production)
|
36
|
+
end
|
37
|
+
|
38
|
+
@options.freeze
|
39
|
+
end
|
40
|
+
|
41
|
+
def verify_check_code(params = {})
|
42
|
+
stringified_keys = params.stringify_keys
|
43
|
+
check_code = stringified_keys.delete('CheckCode')
|
44
|
+
make_check_code(stringified_keys) == check_code
|
45
|
+
end
|
46
|
+
|
47
|
+
def generate_mpg_params(params = {})
|
48
|
+
param_required! params, [:MerchantOrderNo, :Amt, :ItemDesc, :Email, :LoginType]
|
49
|
+
|
50
|
+
post_params = {
|
51
|
+
RespondType: 'String',
|
52
|
+
TimeStamp: Time.now.to_i,
|
53
|
+
Version: '1.2'
|
54
|
+
}.merge!(params)
|
55
|
+
|
56
|
+
generate_params(:mpg, post_params)
|
57
|
+
end
|
58
|
+
|
59
|
+
def query_trade_info(params = {})
|
60
|
+
param_required! params, [:MerchantOrderNo, :Amt]
|
61
|
+
|
62
|
+
post_params = {
|
63
|
+
Version: '1.1',
|
64
|
+
RespondType: 'String',
|
65
|
+
TimeStamp: Time.now.to_i
|
66
|
+
}.merge!(params)
|
67
|
+
|
68
|
+
res = request :query_trade_info, post_params
|
69
|
+
Hash[res.body.split('&').map! { |i| URI.decode(i).split('=') }]
|
70
|
+
end
|
71
|
+
|
72
|
+
def credit_card_deauthorize(params = {})
|
73
|
+
param_required! params, [:Amt, :IndexType]
|
74
|
+
|
75
|
+
raise MissingOption, %(One of the following param is required: MerchantOrderNo, TradeNo) if params[:MerchantOrderNo].nil? && params[:TradeNo].nil?
|
76
|
+
|
77
|
+
post_params = {
|
78
|
+
RespondType: 'String',
|
79
|
+
Version: '1.0',
|
80
|
+
TimeStamp: Time.now.to_i
|
81
|
+
}.merge!(params)
|
82
|
+
|
83
|
+
post_params.delete_if { |_, value| value.nil? }
|
84
|
+
|
85
|
+
res = request :credit_card_deauthorize, post_params
|
86
|
+
Hash[res.body.split('&').map! { |i| URI.decode(i.force_encoding('ASCII-8BIT').force_encoding('UTF-8')).split('=') }]
|
87
|
+
end
|
88
|
+
|
89
|
+
def credit_card_deauthorize_by_merchant_order_no(params = {})
|
90
|
+
param_required! params, [:Amt, :MerchantOrderNo]
|
91
|
+
|
92
|
+
post_params = {
|
93
|
+
IndexType: 1
|
94
|
+
}.merge!(params)
|
95
|
+
|
96
|
+
credit_card_deauthorize post_params
|
97
|
+
end
|
98
|
+
|
99
|
+
def credit_card_deauthorize_by_trade_no(params = {})
|
100
|
+
param_required! params, [:Amt, :TradeNo]
|
101
|
+
|
102
|
+
post_params = {
|
103
|
+
IndexType: 2
|
104
|
+
}.merge!(params)
|
105
|
+
|
106
|
+
credit_card_deauthorize post_params
|
107
|
+
end
|
108
|
+
|
109
|
+
def credit_card_collect_refund(params = {})
|
110
|
+
param_required! params, [:Amt, :IndexType, :CloseType]
|
111
|
+
|
112
|
+
raise MissingOption, %(One of the following param is required: MerchantOrderNo, TradeNo) if params[:MerchantOrderNo].nil? && params[:TradeNo].nil?
|
113
|
+
|
114
|
+
post_params = {
|
115
|
+
RespondType: 'String',
|
116
|
+
Version: '1.0',
|
117
|
+
TimeStamp: Time.now.to_i
|
118
|
+
}.merge!(params)
|
119
|
+
|
120
|
+
res = request :credit_card_collect_refund, post_params
|
121
|
+
Hash[res.body.split('&').map! { |i| URI.decode(i.force_encoding('ASCII-8BIT').force_encoding('UTF-8')).split('=') }]
|
122
|
+
end
|
123
|
+
|
124
|
+
def credit_card_collect_refund_by_merchant_order_no(params = {})
|
125
|
+
param_required! params, [:Amt, :MerchantOrderNo, :CloseType]
|
126
|
+
|
127
|
+
post_params = {
|
128
|
+
IndexType: 1
|
129
|
+
}.merge!(params)
|
130
|
+
|
131
|
+
credit_card_collect_refund post_params
|
132
|
+
end
|
133
|
+
|
134
|
+
def credit_card_collect_refund_by_trade_no(params = {})
|
135
|
+
param_required! params, [:Amt, :TradeNo, :CloseType]
|
136
|
+
|
137
|
+
post_params = {
|
138
|
+
IndexType: 1
|
139
|
+
}.merge!(params)
|
140
|
+
|
141
|
+
credit_card_collect_refund post_params
|
142
|
+
end
|
143
|
+
|
144
|
+
def generate_credit_card_period_params(params = {})
|
145
|
+
param_required! params, [:MerchantOrderNo, :ProdDesc, :PeriodAmt, :PeriodAmtMode, :PeriodType, :PeriodPoint, :PeriodStartType, :PeriodTimes]
|
146
|
+
|
147
|
+
generate_params(:credit_card_period, {
|
148
|
+
RespondType: 'String',
|
149
|
+
TimeStamp: Time.now.to_i,
|
150
|
+
Version: '1.0'
|
151
|
+
}.merge!(params))
|
152
|
+
end
|
153
|
+
|
154
|
+
def make_check_value(type, params = {})
|
155
|
+
case type
|
156
|
+
when :mpg
|
157
|
+
check_value_fields = [:Amt, :MerchantID, :MerchantOrderNo, :TimeStamp, :Version]
|
158
|
+
padded = "HashKey=#{@options[:hash_key]}&%s&HashIV=#{@options[:hash_iv]}"
|
159
|
+
when :query_trade_info
|
160
|
+
check_value_fields = [:Amt, :MerchantID, :MerchantOrderNo]
|
161
|
+
padded = "IV=#{@options[:hash_iv]}&%s&Key=#{@options[:hash_key]}"
|
162
|
+
when :credit_card_period
|
163
|
+
check_value_fields = [:MerchantID, :MerchantOrderNo, :PeriodAmt, :PeriodType, :TimeStamp]
|
164
|
+
padded = "HashKey=#{@options[:hash_key]}&%s&HashIV=#{@options[:hash_iv]}"
|
165
|
+
else
|
166
|
+
raise UnsupportedType, 'Unsupported API type.'
|
167
|
+
end
|
168
|
+
|
169
|
+
param_required! params, check_value_fields
|
170
|
+
|
171
|
+
raw = params.select { |key, _| key.to_s.match(/^(#{check_value_fields.join('|')})$/) }
|
172
|
+
.sort_by { |k, _| k.downcase }.map! { |k, v| "#{k}=#{v}" }.join('&')
|
173
|
+
|
174
|
+
padded = padded % raw
|
175
|
+
|
176
|
+
Digest::SHA256.hexdigest(padded).upcase!
|
177
|
+
end
|
178
|
+
|
179
|
+
def encode_post_data(data)
|
180
|
+
cipher = OpenSSL::Cipher::AES256.new(:CBC)
|
181
|
+
cipher.encrypt
|
182
|
+
cipher.padding = 0
|
183
|
+
cipher.key = @options[:hash_key]
|
184
|
+
cipher.iv = @options[:hash_iv]
|
185
|
+
data = add_padding(data)
|
186
|
+
encrypted = cipher.update(data) + cipher.final
|
187
|
+
encrypted.unpack('H*').first
|
188
|
+
end
|
189
|
+
|
190
|
+
private
|
191
|
+
|
192
|
+
def option_required!(*option_names)
|
193
|
+
option_names.each do |option_name|
|
194
|
+
raise MissingOption, %(option "#{option_name}" is required.) if @options[option_name].nil?
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def param_required!(params, param_names)
|
199
|
+
param_names.each do |param_name|
|
200
|
+
raise MissingParameter, %(param "#{param_name}" is required.) if params[param_name].nil?
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def make_check_code(params = {})
|
205
|
+
raw = params.select { |key, _| key.to_s.match(/^(Amt|MerchantID|MerchantOrderNo|TradeNo)$/) }
|
206
|
+
.sort_by { |k, _| k.downcase }.map! { |k, v| "#{k}=#{v}" }.join('&')
|
207
|
+
padded = "HashIV=#{@options[:hash_iv]}&#{raw}&HashKey=#{@options[:hash_key]}"
|
208
|
+
Digest::SHA256.hexdigest(padded).upcase!
|
209
|
+
end
|
210
|
+
|
211
|
+
def generate_params(type, overwrite_params = {})
|
212
|
+
result = overwrite_params.clone
|
213
|
+
result[:MerchantID] = @options[:merchant_id]
|
214
|
+
result[:CheckValue] = make_check_value(type, result)
|
215
|
+
result
|
216
|
+
end
|
217
|
+
|
218
|
+
def request(type, params = {})
|
219
|
+
case type
|
220
|
+
when :query_trade_info
|
221
|
+
api_url = TRANSACTION_API_ENDPOINTS[@options[:mode]]
|
222
|
+
when :credit_card_deauthorize
|
223
|
+
api_url = CREDITCARD_DEAUTHORIZE_API_ENDPOINTS[@options[:mode]]
|
224
|
+
when :credit_card_collect_refund
|
225
|
+
api_url = CREDITCARD_COLLECT_REFUND_API_ENDPOINTS[@options[:mode]]
|
226
|
+
end
|
227
|
+
|
228
|
+
if NEED_CHECK_VALUE_APIS.include?(type)
|
229
|
+
post_params = generate_params(type, params)
|
230
|
+
else
|
231
|
+
post_params = {
|
232
|
+
MerchantID_: @options[:merchant_id],
|
233
|
+
PostData_: encode_post_data(URI.encode(params.map { |key, value| "#{key}=#{value}" }.join('&')))
|
234
|
+
}
|
235
|
+
end
|
236
|
+
|
237
|
+
Net::HTTP.post_form URI(api_url), post_params
|
238
|
+
end
|
239
|
+
|
240
|
+
def add_padding(text, size = 32)
|
241
|
+
len = text.length
|
242
|
+
pad = size - (len % size)
|
243
|
+
text + (pad.chr * pad)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Spgateway
|
3
|
+
# Generic Spgateway exception class.
|
4
|
+
class SpgatewayError < StandardError; end
|
5
|
+
class MissingOption < SpgatewayError; end
|
6
|
+
class MissingParameter < SpgatewayError; end
|
7
|
+
class InvalidMode < SpgatewayError; end
|
8
|
+
class UnsupportedType < SpgatewayError; end
|
9
|
+
end
|
data/spgateway.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'spgateway/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'spgateway_client'
|
9
|
+
spec.version = Spgateway::VERSION
|
10
|
+
spec.authors = ['Calvert']
|
11
|
+
spec.email = ['']
|
12
|
+
|
13
|
+
spec.summary = '智付通(Spgateway)API 包裝'
|
14
|
+
spec.description = '智付通(Spgateway)API 包裝'
|
15
|
+
spec.homepage = 'https://github.com/CalvertYang/spgateway'
|
16
|
+
spec.license = 'MIT'
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
+
spec.bindir = 'bin'
|
20
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ['lib']
|
22
|
+
|
23
|
+
spec.add_development_dependency 'bundler', '~> 1.11'
|
24
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
25
|
+
spec.add_development_dependency 'minitest', '~> 5.8'
|
26
|
+
spec.add_development_dependency 'minitest-reporters', '~> 1.1'
|
27
|
+
spec.add_development_dependency 'sinatra'
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: spgateway_client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Calvert
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-02-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.11'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.11'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.8'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest-reporters
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.1'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: sinatra
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: 智付通(Spgateway)API 包裝
|
84
|
+
email:
|
85
|
+
- ''
|
86
|
+
executables:
|
87
|
+
- console
|
88
|
+
- setup
|
89
|
+
extensions: []
|
90
|
+
extra_rdoc_files: []
|
91
|
+
files:
|
92
|
+
- ".gitignore"
|
93
|
+
- ".rubocop.yml"
|
94
|
+
- ".travis.yml"
|
95
|
+
- Gemfile
|
96
|
+
- LICENSE.txt
|
97
|
+
- README.md
|
98
|
+
- Rakefile
|
99
|
+
- bin/console
|
100
|
+
- bin/setup
|
101
|
+
- examples/public/style.css
|
102
|
+
- examples/server.rb
|
103
|
+
- examples/views/mpg.erb
|
104
|
+
- examples/views/period.erb
|
105
|
+
- lib/spgateway.rb
|
106
|
+
- lib/spgateway/client.rb
|
107
|
+
- lib/spgateway/core_ext/hash.rb
|
108
|
+
- lib/spgateway/errors.rb
|
109
|
+
- lib/spgateway/version.rb
|
110
|
+
- lib/spgateway_client.rb
|
111
|
+
- spgateway.gemspec
|
112
|
+
homepage: https://github.com/CalvertYang/spgateway
|
113
|
+
licenses:
|
114
|
+
- MIT
|
115
|
+
metadata: {}
|
116
|
+
post_install_message:
|
117
|
+
rdoc_options: []
|
118
|
+
require_paths:
|
119
|
+
- lib
|
120
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - ">="
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0'
|
130
|
+
requirements: []
|
131
|
+
rubyforge_project:
|
132
|
+
rubygems_version: 2.5.2
|
133
|
+
signing_key:
|
134
|
+
specification_version: 4
|
135
|
+
summary: 智付通(Spgateway)API 包裝
|
136
|
+
test_files: []
|