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 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
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .DS_Store
data/.rubocop.yml ADDED
@@ -0,0 +1,6 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.4
3
+ Exclude:
4
+ - tmp/*.rb
5
+ Metrics:
6
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ before_install:
3
+ - gem update --system
4
+ - gem update bundler
5
+ rvm:
6
+ - 2.4.0
7
+ - 2.3.0
8
+ - 2.2.0
9
+ - 2.1.5
10
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ source 'https://rubygems.org'
3
+
4
+ gemspec
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
+ [![Gem Version](https://badge.fury.io/rb/spgateway_client.svg)](https://badge.fury.io/rb/spgateway_client)
2
+ [![Build Status](https://travis-ci.org/CalvertYang/spgateway.svg?branch=master)](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
+ ![Analytics](https://ga-beacon.appspot.com/UA-44933497-3/CalvertYang/spgateway?pixel)
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ require 'bundler/gem_tasks'
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << 'test'
7
+ t.pattern = 'test/**/*_test.rb'
8
+ t.verbose = true
9
+ end
10
+
11
+ desc 'Run tests'
12
+ task default: [:test]
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
@@ -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,15 @@
1
+ table {
2
+ width: 100%;
3
+ }
4
+
5
+ table input {
6
+ width: 100%;
7
+ }
8
+
9
+ table .title {
10
+ width: 10%;
11
+ }
12
+
13
+ input[type="submit"] {
14
+ margin-top: 2em;
15
+ }
@@ -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,6 @@
1
+ # frozen_string_literal: true
2
+ require 'spgateway/version'
3
+ require 'spgateway/client'
4
+
5
+ module Spgateway # :nodoc:
6
+ end
@@ -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,10 @@
1
+ # frozen_string_literal: true
2
+ class Hash # :nodoc:
3
+ def stringify_keys
4
+ result = self.class.new
5
+ each_key do |key|
6
+ result[key.to_s] = self[key]
7
+ end
8
+ result
9
+ end
10
+ 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
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ module Spgateway
3
+ VERSION = '1.0.0'
4
+ end
@@ -0,0 +1,2 @@
1
+ # frozen_string_literal: true
2
+ require 'spgateway'
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: []