spgateway_client 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []