allpay_client 1.0.9 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 45e8efe0cfd77657844f1579f7ba92f0353163ec
4
- data.tar.gz: 9359e09cf6c1a665dc6a1ababe6e749a5ff3726f
3
+ metadata.gz: 289f14a5eea78b627c25e2ba352b8db3fefc0085
4
+ data.tar.gz: 33a5f66099a93de19b3a2d0fded4d3642bf195d5
5
5
  SHA512:
6
- metadata.gz: 7a8b488d46e9c5f8eaa892ebfa177cd8f5be1c35425b91f467dec7c356839ca8baaad0098397bf7c5772f2a2b63cef7fd12c220ed7e810f25afdda3c774afe4c
7
- data.tar.gz: 67779336cc5698abd9a47b413bec97f8fd1031219d281b578d694f3a06c4505ff7250d2ac1c54bfeb625875503a1547e0f95a77e6060bf2ca19d926907d81843
6
+ metadata.gz: 53a307012b9805ee5bd2c2f284d7eeaa6455b71d0b877d5ce00d1dc2bdf8e75910771e3dcc3a7c5d616c5fd3f7acab547d340c5a9061a02296b4f46f449fa952
7
+ data.tar.gz: c1e186805948c1339029b4bab674f2aa506687d7be994a41d07f69157de9f8c3a880eb71cf878dc05a77db55f14cc88af15c155a0315853e33007f3a4c530613
data/README.md CHANGED
@@ -4,6 +4,10 @@
4
4
 
5
5
  這是歐付寶 API 的 Ruby 包裝,更多資訊參考他們的[官方文件](https://www.allpay.com.tw/Content/files/%E5%85%A8%E6%96%B9%E4%BD%8D%E9%87%91%E6%B5%81%E4%BB%8B%E6%8E%A5%E6%8A%80%E8%A1%93%E6%96%87%E4%BB%B6.pdf)。
6
6
 
7
+ - 這不是 Rails 插件,只是個 API 包裝。
8
+ - 使用時只需要傳送需要的參數即可,不用產生檢查碼,`allpay_client` 會自己產生。
9
+ - 錯誤代碼太多且會不斷增加,筆者不另行撰寫,官方也建議查網站上的代碼清單。
10
+
7
11
  ## 安裝
8
12
 
9
13
  ```bash
@@ -13,13 +17,14 @@ gem install allpay_client
13
17
  ## 使用
14
18
 
15
19
  ```ruby
16
- client = Allpay::Client.new({
17
- merchant_id: '2000132',
18
- hash_key: '5294y06JbISpM5x9',
19
- hash_iv: 'v77hoKGq4kWxNNIS',
20
- mode: :test
20
+ test_client = Allpay::Client.new(mode: :test)
21
+ production_client = Allpay::Client.new({
22
+ merchant_id: 'MERCHANT_ID',
23
+ hash_key: 'HASH_KEY',
24
+ hash_iv: 'HASH_IV'
21
25
  })
22
- client.request '/Cashier/QueryTradeInfo',
26
+
27
+ test_client.request '/Cashier/QueryTradeInfo',
23
28
  MerchantTradeNo: '0457ce27',
24
29
  TimeStamp: Time.now.to_i
25
30
  ```
@@ -32,10 +37,10 @@ client.request '/Cashier/QueryTradeInfo',
32
37
  - /CreditDetail/DoAction
33
38
  - /Cashier/AioChargeback
34
39
 
35
- 每個 API 有哪些參數建議直接參考歐付寶文件,不過要注意幾點:
40
+ 每個 API 有哪些參數建議直接參考歐付寶文件,注意幾點:
36
41
 
37
- - 原本 API 都需要 `MerchantID` 與 `CheckMacValue`,不過 `Client#request` 已經都處理好了,使用時可忽略這兩個參數,正如上述範例一樣。
38
- - `/Cashier/AioCheckOut` 回傳的內容是 HTML,這個請求應該是交給瀏覽器發送的,所以不應該寫出 `client.request '/Cashier/AioCheckOut'`這樣的內容。
42
+ - 使用時不用煩惱 `MerchantID` 與 `CheckMacValue`,正如上述範例一樣。
43
+ - `/Cashier/AioCheckOut` 回傳的內容是 HTML,這個請求應該是交給瀏覽器發送的,所以不應該寫出 `client.request '/Cashier/AioCheckOut'` 這樣的內容。
39
44
 
40
45
  ## Allpay::Client
41
46
 
@@ -43,8 +48,10 @@ client.request '/Cashier/QueryTradeInfo',
43
48
  --- | --- | ---
44
49
  `request(path, **params)` | `Net::HTTPResponse` | 發送 API 請求
45
50
  `make_mac(**params)` | `String` | 用於產生 `CheckMacValue`,單純做加密,`params` 需要完整包含到 `MerchantID`
51
+ `verify_mac(**params)` | `Boolean` | 會於檢查收到的參數,其檢查碼是否正確,這用在歐付寶物的 `ReturnURL` 與 `PeriodReturnURL` 參數上。
46
52
  `query_trade_info(merchant_trade_number, platform = nil)` | `Hash` | `/Cashier/QueryTradeInfo` 的捷徑方法,將 `TimeStamp` 設定為當前時間
47
53
  `query_period_credit_card_trade_info(merchant_trade_number)` | `Hash` | `/Cashier/QueryPeriodCreditCardTradeInfo` 的捷徑方法,將 `TimeStamp` 設定為當前時間
54
+ `generate_checkout_params` | `Hash` | 用於產生 `/Cashier/AioCheckOut` 表單需要的參數,`MerchantTradeDate`、`MerchantTradeNo`、`PaymentType`,可省略。
48
55
 
49
56
  ## 使用範例
50
57
 
data/examples/server.rb CHANGED
@@ -4,12 +4,9 @@ require 'sinatra'
4
4
  require 'allpay'
5
5
 
6
6
  get '/' do
7
- client = Allpay::Client.new(merchant_id: '2000132', hash_key: '5294y06JbISpM5x9', hash_iv: 'v77hoKGq4kWxNNIS', mode: :test)
8
- @params = {
9
- MerchantID: client.merchant_id,
7
+ client = Allpay::Client.new(mode: :test)
8
+ @params = client.generate_checkout_params({
10
9
  MerchantTradeNo: SecureRandom.hex(4),
11
- MerchantTradeDate: Time.now.strftime('%Y/%m/%d %H:%M:%S'),
12
- PaymentType: 'aio',
13
10
  TotalAmount: 1000,
14
11
  TradeDesc: '腦袋有動工作室',
15
12
  ItemName: '物品一#物品二',
@@ -21,7 +18,6 @@ get '/' do
21
18
  Frequency: 1,
22
19
  ExecTimes: 12,
23
20
  PeriodReturnURL: 'http://requestb.in/158bu8e1'
24
- }
25
- @mac = client.make_mac(@params)
21
+ })
26
22
  erb :index
27
23
  end
@@ -6,23 +6,9 @@
6
6
  </head>
7
7
  <body>
8
8
  <form action="http://payment-stage.allpay.com.tw/Cashier/AioCheckOut" method="post">
9
- <input type="text" name="MerchantID" value="<%= @params[:MerchantID] %>">
10
- <input type="text" name="MerchantTradeNo" value="<%= @params[:MerchantTradeNo] %>">
11
- <input type="text" name="MerchantTradeDate" value="<%= @params[:MerchantTradeDate] %>">
12
- <input type="text" name="PaymentType" value="<%= @params[:PaymentType] %>">
13
- <input type="text" name="TotalAmount" value="<%= @params[:TotalAmount] %>">
14
- <input type="text" name="TradeDesc" value="<%= @params[:TradeDesc] %>">
15
- <input type="text" name="ItemName" value="<%= @params[:ItemName] %>">
16
- <input type="text" name="ReturnURL" value="<%= @params[:ReturnURL] %>">
17
- <input type="text" name="ClientBackURL" value="<%= @params[:ClientBackURL] %>">
18
- <input type="text" name="ChoosePayment" value="<%= @params[:ChoosePayment] %>">
19
- <input type="text" name="PeriodAmount" value="<%= @params[:PeriodAmount] %>">
20
- <input type="text" name="PeriodType" value="<%= @params[:PeriodType] %>">
21
- <input type="text" name="Frequency" value="<%= @params[:Frequency] %>">
22
- <input type="text" name="ExecTimes" value="<%= @params[:ExecTimes] %>">
23
- <input type="text" name="PeriodReturnURL" value="<%= @params[:PeriodReturnURL] %>">
24
-
25
- <input type="text" name="CheckMacValue" value="<%= @mac %>">
9
+ <% @params.each do |k, v| %>
10
+ <input type="text" name="<%= k %>" value="<%= v %>">
11
+ <% end %>
26
12
  <input type="submit">
27
13
  </form>
28
14
  </body>
data/lib/allpay.rb CHANGED
@@ -1,5 +1,5 @@
1
- require "allpay/version"
2
- require "allpay/client"
1
+ require 'allpay/version'
2
+ require 'allpay/client'
3
3
 
4
4
  module Allpay
5
5
  end
data/lib/allpay/client.rb CHANGED
@@ -1,46 +1,73 @@
1
1
  require 'net/http'
2
2
  require 'json'
3
+ require 'cgi'
4
+ require 'digest'
5
+ require 'allpay/errors'
6
+ require 'allpay/core_ext/hash'
3
7
 
4
8
  module Allpay
5
9
  class Client
6
10
  PRODUCTION_API_HOST = 'https://payment.allpay.com.tw'.freeze
7
11
  TEST_API_HOST = 'http://payment-stage.allpay.com.tw'.freeze
12
+ TEST_OPTIONS = {
13
+ merchant_id: '2000132',
14
+ hash_key: '5294y06JbISpM5x9',
15
+ hash_iv: 'v77hoKGq4kWxNNIS'
16
+ }.freeze
8
17
 
9
- attr_accessor :merchant_id, :hash_key, :hash_iv, :mode
18
+ attr_reader :options
10
19
 
11
- def initialize params = {}
12
- merchant_id = params[:merchant_id]
13
- hash_key = params[:hash_key]
14
- hash_iv = params[:hash_iv]
15
- mode = params[:mode]
16
- @merchant_id, @hash_key, @hash_iv, @mode = merchant_id, hash_key, hash_iv, mode
20
+ def initialize options = {}
21
+ @options = {mode: :production}.merge!(options)
22
+ case @options[:mode]
23
+ when :production
24
+ option_required! :merchant_id, :hash_key, :hash_iv
25
+ when :test
26
+ @options = TEST_OPTIONS.merge(options)
27
+ else
28
+ raise InvalidMode, %Q{option :mode is either :test or :production}
29
+ end
30
+ @options.freeze
17
31
  end
18
32
 
19
33
  def api_host
20
- case mode
34
+ case @options[:mode]
21
35
  when :production then PRODUCTION_API_HOST
22
36
  when :test then TEST_API_HOST
23
- else raise '`mode` is either :test or :production (default)'
24
37
  end
25
38
  end
26
39
 
27
40
  def make_mac params = {}
28
41
  raw = params.sort.map!{|k,v| "#{k}=#{v}"}.join('&')
29
- padded = "HashKey=#{@hash_key}&#{raw}&HashIV=#{@hash_iv}"
42
+ padded = "HashKey=#{@options[:hash_key]}&#{raw}&HashIV=#{@options[:hash_iv]}"
30
43
  url_encoded = CGI.escape(padded).downcase!
31
44
  Digest::MD5.hexdigest(url_encoded).upcase!
32
45
  end
33
46
 
34
47
  def verify_mac params = {}
35
- check_mac_value = params[:CheckMacValue] || params['CheckMacValue']
36
- make_mac(params.reject{ |k,v| [:CheckMacValue, 'CheckMacValue'].include? k }) == check_mac_value
48
+ stringified_keys = params.stringify_keys
49
+ check_mac_value = stringified_keys.delete('CheckMacValue')
50
+ make_mac(stringified_keys) == check_mac_value
51
+ end
52
+
53
+ def generate_params overwrite_params = {}
54
+ result = overwrite_params.clone
55
+ result[:MerchantID] = @options[:merchant_id]
56
+ result[:CheckMacValue] = make_mac(result)
57
+ result
58
+ end
59
+
60
+ def generate_checkout_params overwrite_params = {}
61
+ generate_params({
62
+ MerchantTradeDate: Time.now.strftime('%Y/%m/%d %H:%M:%S'),
63
+ MerchantTradeNo: SecureRandom.hex(4),
64
+ PaymentType: 'aio'
65
+ }.merge!(overwrite_params))
37
66
  end
38
67
 
39
68
  def request path, params = {}
40
- params[:MerchantID] = @merchant_id
41
- params[:CheckMacValue] = make_mac(params)
42
69
  api_url = URI.join(api_host, path)
43
- Net::HTTP.post_form api_url, params
70
+ Net::HTTP.post_form api_url, generate_params(params)
44
71
  end
45
72
 
46
73
  def query_trade_info merchant_trade_number, platform = nil
@@ -60,5 +87,13 @@ module Allpay
60
87
  TimeStamp: Time.now.to_i
61
88
  JSON.parse(res.body)
62
89
  end
90
+
91
+ private
92
+
93
+ def option_required! *option_names
94
+ option_names.each do |option_name|
95
+ raise MissingOption, %Q{option "#{option_name}" is required.} if @options[:option_name].nil?
96
+ end
97
+ end
63
98
  end
64
99
  end
@@ -0,0 +1,9 @@
1
+ class Hash
2
+ def stringify_keys
3
+ result = self.class.new
4
+ each_key do |key|
5
+ result[key.to_s] = self[key]
6
+ end
7
+ result
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ module Allpay
2
+ # Generic Allpay exception class.
3
+ class AllpayError < StandardError; end
4
+ class MissingOption < AllpayError; end
5
+ class InvalidMode < AllpayError; end
6
+ end
@@ -1,3 +1,3 @@
1
1
  module Allpay
2
- VERSION = "1.0.9"
2
+ VERSION = "2.0.0"
3
3
  end
data/spec/allpay_spec.rb CHANGED
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  require 'securerandom'
4
4
  describe Allpay::Client do
5
5
  before :all do
6
- @client = Allpay::Client.new(merchant_id: '2000132', hash_key: '5294y06JbISpM5x9', hash_iv: 'v77hoKGq4kWxNNIS', mode: :test)
6
+ @client = Allpay::Client.new(mode: :test)
7
7
  end
8
8
 
9
9
  it '#api /Cashier/AioCheckOut' do
@@ -41,7 +41,7 @@ describe Allpay::Client do
41
41
  end
42
42
 
43
43
  it '#make_mac' do
44
- client = Allpay::Client.new(merchant_id: '12345678', hash_key: 'xdfaefasdfasdfa32d', hash_iv: 'sdfxfafaeafwexfe')
44
+ client = Allpay::Client.new(merchant_id: '12345678', hash_key: 'xdfaefasdfasdfa32d', hash_iv: 'sdfxfafaeafwexfe', mode: :test)
45
45
  mac = client.make_mac({
46
46
  ItemName: 'sdfasdfa',
47
47
  MerchantID: '12345678',
@@ -70,20 +70,4 @@ describe Allpay::Client do
70
70
  MerchantTradeNo: '355313'
71
71
  expect(result).to eq true
72
72
  end
73
-
74
- it '#verify_mac with string hash' do
75
- result = @client.verify_mac 'RtnCode' => '1',
76
- 'PaymentType' => 'Credit_CreditCard',
77
- 'TradeAmt' => '700',
78
- 'PaymentTypeChargeFee' => '14',
79
- 'PaymentDate' => '2015/02/07 14:21:00',
80
- 'SimulatePaid' => '0',
81
- 'CheckMacValue' => '3AF270CCCFA58CA0349F4FD462E21643',
82
- 'TradeDate' => '2015/02/07 14:20:47',
83
- 'MerchantID' => '2000132',
84
- 'TradeNo' => '1502071420478656',
85
- 'RtnMsg' => '交易成功',
86
- 'MerchantTradeNo' => '355313'
87
- expect(result).to eq true
88
- end
89
73
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: allpay_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.9
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jian Weihang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-08 00:00:00.000000000 Z
11
+ date: 2015-03-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -85,6 +85,8 @@ files:
85
85
  - examples/views/index.erb
86
86
  - lib/allpay.rb
87
87
  - lib/allpay/client.rb
88
+ - lib/allpay/core_ext/hash.rb
89
+ - lib/allpay/errors.rb
88
90
  - lib/allpay/version.rb
89
91
  - lib/allpay_client.rb
90
92
  - spec/allpay_spec.rb