newebpay-rails 2.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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +3 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +7 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/Gemfile +6 -0
  8. data/Gemfile.lock +234 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +12 -0
  11. data/Rakefile +12 -0
  12. data/app/controllers/newebpay/application_controller.rb +23 -0
  13. data/app/controllers/newebpay/cancel_auth_notify_callbacks_controller.rb +12 -0
  14. data/app/controllers/newebpay/donation_notify_callbacks_controller.rb +12 -0
  15. data/app/controllers/newebpay/mpg_callbacks_controller.rb +23 -0
  16. data/app/controllers/newebpay/notify_callbacks_controller.rb +18 -0
  17. data/app/controllers/newebpay/payment_code_callbacks_controller.rb +18 -0
  18. data/app/controllers/newebpay/periodical_callbacks_controller.rb +23 -0
  19. data/app/controllers/newebpay/periodical_notify_callbacks_controller.rb +23 -0
  20. data/app/helpers/newebpay/application_helper.rb +7 -0
  21. data/app/helpers/newebpay/donation_helper.rb +36 -0
  22. data/app/helpers/newebpay/mpg_helper.rb +35 -0
  23. data/app/helpers/newebpay/periodical_helper.rb +35 -0
  24. data/app/jobs/newebpay/rails/application_job.rb +6 -0
  25. data/bin/rails +13 -0
  26. data/config/initializers/inflections.rb +3 -0
  27. data/config/routes.rb +13 -0
  28. data/lib/generators/newebpay/install_generator.rb +19 -0
  29. data/lib/generators/newebpay/templates/newebpay_initializer.rb +117 -0
  30. data/lib/newebpay.rb +46 -0
  31. data/lib/newebpay/attr_key_helper.rb +7 -0
  32. data/lib/newebpay/bank_codes.rb +17 -0
  33. data/lib/newebpay/cancel_auth/request.rb +69 -0
  34. data/lib/newebpay/cancel_auth/response.rb +38 -0
  35. data/lib/newebpay/close_fund/base.rb +69 -0
  36. data/lib/newebpay/close_fund/refund.rb +10 -0
  37. data/lib/newebpay/close_fund/request.rb +10 -0
  38. data/lib/newebpay/close_fund/response.rb +26 -0
  39. data/lib/newebpay/config.rb +65 -0
  40. data/lib/newebpay/donation/form.rb +90 -0
  41. data/lib/newebpay/donation/response.rb +38 -0
  42. data/lib/newebpay/engine.rb +9 -0
  43. data/lib/newebpay/error_codes.rb +131 -0
  44. data/lib/newebpay/mpg/form.rb +92 -0
  45. data/lib/newebpay/mpg/response.rb +27 -0
  46. data/lib/newebpay/newebpay_helper.rb +98 -0
  47. data/lib/newebpay/periodical/form.rb +83 -0
  48. data/lib/newebpay/periodical/response.rb +25 -0
  49. data/lib/newebpay/query_trade/response.rb +35 -0
  50. data/lib/newebpay/query_trade/search.rb +53 -0
  51. data/lib/newebpay/rails.rb +6 -0
  52. data/lib/newebpay/rails/engine.rb +9 -0
  53. data/lib/newebpay/version.rb +3 -0
  54. data/newebpay-rails.gemspec +50 -0
  55. metadata +233 -0
@@ -0,0 +1,9 @@
1
+ module Newebpay
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Newebpay
4
+
5
+ config.to_prepare do
6
+ ::ApplicationController.helper(Newebpay::Engine.helpers)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,131 @@
1
+ module Newebpay
2
+
3
+ class ErrorCodes
4
+ @@error_codes = {
5
+ DON10001: "商店代號不可空白",
6
+ DON10002: "串接商店代號與後台設定不符",
7
+ DON10003: "捐款編號不可空白",
8
+ DON10004: "捐款編號格式錯誤! 格式為:英數及底線",
9
+ DON10005: "捐款編號不可重複",
10
+ DON10006: "金額檢查錯誤",
11
+ DON10007: "NotifyURL 不可空白",
12
+ DON10008: "時間戳記不可空白",
13
+ DON10009: "商品說明不可空白",
14
+ DON10010: "CheckValue 檢查錯誤",
15
+ DON10011: "電話號碼格式有錯誤,請檢查",
16
+ DON10012: "E-Mail 信箱格式有錯誤,請檢查",
17
+ DON10013: "統一編號格式錯誤",
18
+ DON10014: "統一編號輸入有誤,請檢查",
19
+ DON10015: "身份證字號有錯誤,請檢查",
20
+ DON10016: "藍新金流商店金流尚未設定開啟",
21
+ DON10017: "CheckValue 檢查失敗",
22
+ MEM40008: "資料空白",
23
+ MEM40012: "資料傳遞錯誤PostData_ 空白",
24
+ MEM40013: "資料不齊全",
25
+ MEM40014: "傳送時間有誤",
26
+ MPG01001: "會員參數 不可空白/設定錯誤",
27
+ MPG01002: "時間戳記不可空白",
28
+ MPG01005: "TokenTerm 不可空白/設定錯誤",
29
+ MPG01008: "分期參數設定錯誤",
30
+ MPG01009: "商店代號不可空白",
31
+ MPG01010: "程式版本設定錯誤",
32
+ MPG01011: "回傳規格設定錯誤",
33
+ MPG01012: "商店訂單編號不可空白/設定錯誤",
34
+ MPG01013: "付款人電子信箱設定錯誤",
35
+ MPG01014: "網址設定錯誤",
36
+ MPG01015: "訂單金額不可空白/設定錯誤",
37
+ MPG01017: "商品資訊不可空白",
38
+ MPG01018: "繳費有效期限設定錯誤",
39
+ MPG01023: "交易加密資料不可空白",
40
+ MPG01024: "交易加密 SHA 資料不可空白",
41
+ MPG02002: "查無商店開啟任何金流服務",
42
+ MPG02003: "支付方式未啟用,請洽客服中心",
43
+ MPG02004: "送出後檢查,超過交易限制秒數",
44
+ MPG02005: "送出後檢查,驗證資料錯誤",
45
+ MPG02006: "系統發生異常,請洽客服中心",
46
+ MPG03001: "FormPost 加密失敗",
47
+ MPG03002: "拒絕交易 IP",
48
+ MPG03003: "IP 交易次數限制",
49
+ MPG03004: "商店狀態已被暫停或是關閉,無法進行交易",
50
+ MPG03007: "查無此商店代號",
51
+ MPG03008: "已存在相同的商店訂單編號",
52
+ MPG03009: "交易失敗",
53
+ PER10001: "商店資料取得失敗",
54
+ PER10002: "資料解密錯誤",
55
+ PER10003: "POST 資料傳遞錯誤",
56
+ PER10004: "資料不齊全",
57
+ PER10005: "資料不可空白",
58
+ PER10006: "商品名稱不得含有 JavaScript 語法、CSS 語法",
59
+ PER10007: "委託金額格式不對,金額必須為數字",
60
+ PER10008: "委託金額不能為零",
61
+ PER10009: "週期設定錯誤! (W=週,M=月,Y=年)",
62
+ PER10010: "商店訂單編號錯誤,只允許英數與底線",
63
+ PER10011: "商店訂單編號長度限制為 20 字",
64
+ PER10012: "回傳格式格式錯誤,只接受 JSON 或 String",
65
+ PER10013: "週期授權時間資料不正確,日期格式為 1 到 7",
66
+ PER10014: "週期授權時間資料不正確,日期格式為 1 到 7(長度不符)",
67
+ PER10015: "定期授權時間資料不正確,日期格式為 01 到 31",
68
+ PER10016: "定期授權時間資料不正確,日期格式為 01 到 31(長度不符)",
69
+ PER10017: "定期授權時間資料不正確,日期格式為 01 到 31",
70
+ PER10018: "定期授權時間資料不正確,日期格式為 01 到 31",
71
+ PER10019: "定期授權時間資料不正確,長度不符",
72
+ PER10020: "首期授權模式設定錯誤(1-3),請檢查",
73
+ PER10021: "備註說明不得含有 JavaScript 語法、CSS 語法",
74
+ PER10022: "授權期數格式不對,必須為數字",
75
+ PER10023: "授權期數不能為零",
76
+ PER10024: "授權期數不能多於 999 次",
77
+ PER10025: "返回商店網址格式錯誤",
78
+ PER10026: "每期授權通知網址格式錯誤",
79
+ PER10027: "是否開啟付款人資訊設定錯誤",
80
+ PER10028: "付款人電子信箱格式錯誤",
81
+ PER10029: "商店代號停用",
82
+ PER10030: "商店信用卡資格停用",
83
+ PER10031: "商店定期定額資格停用",
84
+ PER10032: "該訂單編號已重覆",
85
+ PER10033: "寫入委託單失敗",
86
+ PER10034: "授權失敗,委託單建立失敗",
87
+ PER10035: "委託單更新授權結果失敗",
88
+ PER10036: "驗證資料錯誤(來源不合法)",
89
+ PER10037: "付款頁參數不足",
90
+ TRA10001: "商店代號錯誤",
91
+ TRA10003: "金額必須為數字且大於 0 元",
92
+ TRA10008: "資料加密錯誤",
93
+ TRA10009: "商店代號空白",
94
+ TRA10012: "商店代號停用",
95
+ TRA10013: "商店信用卡資格停用",
96
+ TRA10015: "網路連線失敗",
97
+ TRA10021: "查無該筆交易資訊",
98
+ TRA10032: "單號類別錯誤",
99
+ TRA10033: "商店訂單編號空白或藍新金流交易序號空白",
100
+ TRA10036: "RespondType 欄位資料格式錯誤",
101
+ TRA10037: "商店訂單編號錯誤",
102
+ TRA10038: "藍新金流交易序號格式錯誤",
103
+ TRA10041: "NotifyURL 網址格式錯誤",
104
+ TRA10045: "該筆交易今日已退款",
105
+ TRA10047: "交易非授權成功狀態",
106
+ TRA10048: "該交易正在請款狀態",
107
+ TRA10049: "該交易正在退款狀態",
108
+ TRA10050: "金額不符",
109
+ TRA10051: "取消授權失敗",
110
+ TRA10054: "檢查碼 CheckValue 有錯誤",
111
+ TRA10058: "分期交易非全額請款或退款",
112
+ TRA10094: "取消請款或退款時查無該筆資料,或該筆資料已不可取消請款或退款",
113
+ TRA10095: "此筆交易已過關帳時間,不可取消",
114
+ TRA20001: "金融機構取消授權批次處理中",
115
+ TRA20004: "商店訂單編號重覆",
116
+ TRA20011: "該筆交易已請款",
117
+ TRA40008: "資料不可空白",
118
+ TRA40012: "資料傳遞錯誤",
119
+ TRA40013: "資料不齊全",
120
+ TRA40014: "TimeStamp 欄位錯誤"
121
+ }
122
+
123
+ def self.error_codes
124
+ @@error_codes
125
+ end
126
+
127
+ # def self.get_error_message code
128
+ # @error_code[code]
129
+ # end
130
+ end
131
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Newebpay::MPG
4
+ class Form
5
+ attr_accessor :attrs, :merchant_id
6
+ REQUIRED_ATTRS = %i[order_number description price email payment_methods].freeze
7
+ PAYMENT_METHODS = %i[credit credit_red unionpay webatm vacc cvs barcode android_pay samsung_pay p2g].freeze
8
+ def initialize(options)
9
+ check_valid(options)
10
+ @attrs = {}
11
+ @merchant_id = options[:merchant_id] || Newebpay.config.merchant_id
12
+ parse_attr(options)
13
+
14
+ @attrs['Version'] = version
15
+ @attrs['TimeStamp'] = Time.now.to_i
16
+ @attrs['RespondType'] = 'JSON'
17
+ end
18
+
19
+ def form_attrs
20
+ @form_attrs ||= {
21
+ MerchantID: merchant_id,
22
+ TradeInfo: trade_info,
23
+ TradeSha: trade_sha,
24
+ Version: version
25
+ }
26
+ end
27
+
28
+ def trade_info
29
+ @trade_info ||= Newebpay::NewebpayHelper.encrypt_data(encode_url_params)
30
+ end
31
+
32
+ def trade_sha
33
+ @trade_sha ||= Newebpay::NewebpayHelper.sha256_encode_trade_info(trade_info)
34
+ end
35
+
36
+ def encode_url_params
37
+ URI.encode_www_form(attrs)
38
+ end
39
+
40
+ def version
41
+ '1.5'
42
+ end
43
+
44
+ private
45
+
46
+ def parse_attr(options)
47
+ attrs[:MerchantID] = merchant_id
48
+ attrs[:MerchantOrderNo] = options[:order_number]
49
+ attrs[:ItemDesc] = options[:description]
50
+ attrs[:Amt] = options[:price]
51
+ attrs[:Email] = options[:email]
52
+ attrs[:CVSCOM] = options[:cvscom]
53
+ attrs[:LoginType] = options[:login_required] ? '1' : '0'
54
+ attrs[:LangType] = options[:locale] || 'zh-tw'
55
+ attrs[:TradeLimit] = options[:trade_limit]
56
+ attrs[:ExpireDate] = options[:expire_date]
57
+ attrs[:ClientBackURL] = options[:cancel_url]
58
+ attrs[:OrderComment] = options[:comment]
59
+ attrs[:EmailModify] = options[:email_editable] ? '1' : '0'
60
+ attrs[:InstFlag] = options[:inst_flag]
61
+ attrs[:ReturnURL] = Newebpay::Engine.routes.url_helpers.mpg_callbacks_url(host: Newebpay.host)
62
+ attrs[:CustomerURL] = Newebpay::Engine.routes.url_helpers.payment_code_callbacks_url(host: Newebpay.host) if Newebpay.config.payment_code_callback
63
+ attrs[:NotifyURL] = Newebpay::Engine.routes.url_helpers.notify_callbacks_url(host: Newebpay.host) if Newebpay.config.notify_callback
64
+
65
+ options[:payment_methods].each do |payment_method|
66
+ if payment_method == :credit_red
67
+ attrs[:CreditRed] = '1'
68
+ else
69
+ attrs[payment_method.upcase] = '1'
70
+ end
71
+ end
72
+ end
73
+
74
+ def check_valid(options)
75
+ unless options.is_a? Hash
76
+ raise ArgumentError, "When initializing #{self.class.name}, you must pass a hash."
77
+ end
78
+
79
+ REQUIRED_ATTRS.each do |argument|
80
+ raise ArgumentError, "Missing required argument: #{argument}." unless options[argument]
81
+ end
82
+
83
+ unless options[:payment_methods].is_a? Array
84
+ raise ArgumentError, 'payment_methods must be an Array'
85
+ end
86
+
87
+ if (options[:payment_methods] - PAYMENT_METHODS).any?
88
+ raise ArgumentError, 'Invalid payment method'
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Newebpay::MPG
4
+ class Response
5
+ attr_reader :status, :message, :trade_info, :trade_sha
6
+ def initialize(response_params)
7
+ raw_params = Newebpay::NewebpayHelper.decrypt_data(response_params)
8
+
9
+ response_data = JSON.parse(raw_params)
10
+ @status = response_data['Status']
11
+ @message = response_data['Message']
12
+ @trade_info = response_data['TradeInfo']
13
+ @trade_sha = response_data['TradeSha']
14
+ @result = response_data['Result']
15
+
16
+ @result.each do |key, values|
17
+ define_singleton_method(key.underscore) do
18
+ values
19
+ end
20
+ end
21
+ end
22
+
23
+ def success?
24
+ status == 'SUCCESS'
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+ require 'base64'
5
+ require 'digest'
6
+
7
+ module Newebpay
8
+ module NewebpayHelper
9
+ # for mpg
10
+ def self.encrypt_data(data)
11
+ config = Newebpay.config
12
+ encrypt config.hash_key, config.hash_iv, data
13
+ end
14
+
15
+ def self.encrypt_merchant_data(data)
16
+ config = Newebpay.merchant_config
17
+ encrypt config.hash_key, config.hash_iv, data
18
+ end
19
+
20
+ def self.encrypt(key, iv, data)
21
+ cipher = OpenSSL::Cipher::AES256.new(:CBC)
22
+ cipher.encrypt
23
+ cipher.padding = 0
24
+ cipher.key = key
25
+ cipher.iv = iv
26
+ padding_data = add_padding(data)
27
+ encrypted = cipher.update(padding_data) + cipher.final
28
+ encrypted.unpack('H*').first
29
+ end
30
+
31
+ def self.add_padding(data, block_size = 32)
32
+ pad = block_size - (data.length % block_size)
33
+ data + (pad.chr * pad)
34
+ end
35
+
36
+ def self.decrypt_merchant_data(encrypted_data)
37
+ config = Newebpay.merchant_config
38
+ decrypt config.hash_key, config.hash_iv, encrypted_data
39
+ end
40
+
41
+ def self.decrypt_data(encrypted_data)
42
+ config = Newebpay.config
43
+ decrypt config.hash_key, config.hash_iv, encrypted_data
44
+ end
45
+
46
+ def self.decrypt(key, iv, encrypted_data)
47
+ encrypted_data = [encrypted_data].pack('H*')
48
+ decipher = OpenSSL::Cipher::AES256.new(:CBC)
49
+ decipher.decrypt
50
+ decipher.padding = 0
51
+ decipher.key = key
52
+ decipher.iv = iv
53
+ data = decipher.update(encrypted_data) + decipher.final
54
+ strippadding data
55
+ end
56
+
57
+ # for mpg
58
+ def self.sha256_encode_trade_info(trade_info)
59
+ config = Newebpay.config
60
+ sha256_encode config.hash_key, config.hash_iv, trade_info
61
+ end
62
+
63
+ # to do 加密方式混亂,待整合與重新命名
64
+ def self.create_check_value(check_string)
65
+ # query_trade
66
+ config = Newebpay.config
67
+ encode_string = "IV=#{config.hash_iv}&#{check_string}&Key=#{config.hash_key}"
68
+ Digest::SHA256.hexdigest(encode_string).upcase
69
+ end
70
+
71
+ def self.create_check_code(check_string)
72
+ # query_trade
73
+ config = Newebpay.config
74
+ encode_string = "HashIV=#{config.hash_iv}&#{check_string}&HashKey=#{config.hash_key}"
75
+ Digest::SHA256.hexdigest(encode_string).upcase
76
+ end
77
+
78
+ def self.sha256_encode(key, iv, trade_info, hash_iv_first = false)
79
+ encode_string = if hash_iv_first
80
+ "HashIV=#{iv}&#{trade_info}&HashKey=#{key}"
81
+ else
82
+ "HashKey=#{key}&#{trade_info}&HashIV=#{iv}"
83
+ end
84
+ Digest::SHA256.hexdigest(encode_string).upcase
85
+ end
86
+
87
+ def self.strippadding(data)
88
+ slast = data[-1].ord
89
+ slastc = slast.chr
90
+ padding_index = /#{slastc}{#{slast}}/ =~ data
91
+ if !padding_index.nil?
92
+ data[0, padding_index]
93
+ else
94
+ false
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'newebpay/attr_key_helper'
4
+
5
+ module Newebpay::Periodical
6
+ class Form
7
+ REQUIRED_ATTRS = %i[order_number description price email].freeze
8
+ attr_accessor :attrs, :merchant_id
9
+ def initialize(options)
10
+ check_valid(options)
11
+ @attrs = {}
12
+ @merchant_id = options[:merchant_id] || Newebpay.config.merchant_id
13
+ parse_attr(options)
14
+
15
+ @attrs['Version'] = version
16
+ @attrs['TimeStamp'] = Time.now.to_i
17
+ @attrs['RespondType'] = 'JSON'
18
+ end
19
+
20
+ def form_attrs
21
+ @form_attrs ||= {
22
+ MerchantID_: merchant_id,
23
+ PostData_: trade_info
24
+ }
25
+ end
26
+
27
+ def trade_info
28
+ @trade_info ||= Newebpay::NewebpayHelper.encrypt_data(encode_url_params)
29
+ end
30
+
31
+ def encode_url_params
32
+ URI.encode_www_form(attrs)
33
+ end
34
+
35
+ def version
36
+ '1.0'
37
+ end
38
+
39
+ private
40
+
41
+ def parse_attr(options)
42
+ attrs[:MerOrderNo] = options[:order_number]
43
+ attrs[:ProdDesc] = options[:description]
44
+ attrs[:PeriodAmt] = options[:price]
45
+ attrs[:PayerEmail] = options[:email]
46
+ attrs[:PeriodPoint] = options[:period_point] || '01'
47
+ attrs[:PeriodTimes] = options[:period_times] || '99'
48
+ attrs[:PeriodStartType] = options[:check_type] || '1'
49
+ attrs[:MerchantID] = merchant_id
50
+ attrs[:PeriodMemo] = options[:comment]
51
+ attrs[:PaymentInfo] = options[:payment_info] || 'N'
52
+ attrs[:OrderInfo] = options[:order_info] || 'N'
53
+ attrs[:EmailModify] = options[:email_editable] || '0'
54
+ attrs[:BackURL] = options[:cancel_url]
55
+ attrs[:ReturnURL] = Newebpay::Engine.routes.url_helpers.periodical_callbacks_url(host: Newebpay.host) if Newebpay.config.periodical_callback
56
+ attrs[:NotifyURL] = Newebpay::Engine.routes.url_helpers.periodical_notify_callbacks_url(host: Newebpay.host) if Newebpay.config.periodical_notify_callback
57
+
58
+ options[:period_type] ||= :monthly
59
+ case options[:period_type]
60
+ when :daily
61
+ attrs[:PeriodType] = 'D'
62
+ when :weekly
63
+ attrs[:PeriodType] = 'W'
64
+ when :monthly
65
+ attrs[:PeriodType] = 'M'
66
+ when :yearly
67
+ attrs[:PeriodType] = 'Y'
68
+ else
69
+ raise ArgumentError, "Invalid period_type: #{options[:period_type]}"
70
+ end
71
+ end
72
+
73
+ def check_valid(options)
74
+ unless options.is_a? Hash
75
+ raise ArgumentError, "When initializing #{self.class.name}, you must pass a hash."
76
+ end
77
+
78
+ REQUIRED_ATTRS.each do |argument|
79
+ raise ArgumentError, "Missing required argument: #{argument}." unless options[argument]
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Newebpay::Periodical
4
+ class Response
5
+ attr_reader :status, :message
6
+ def initialize(response_params)
7
+ raw_params = Newebpay::NewebpayHelper.decrypt_data(response_params)
8
+
9
+ response_data = JSON.parse(raw_params)
10
+ @status = response_data['Status']
11
+ @message = response_data['Message']
12
+ @result = response_data['Result']
13
+
14
+ @result.each do |key, values|
15
+ define_singleton_method(key.underscore) do
16
+ values
17
+ end
18
+ end
19
+ end
20
+
21
+ def success?
22
+ status == 'SUCCESS'
23
+ end
24
+ end
25
+ end