tappay_ruby 0.6.0 → 0.8.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
  SHA256:
3
- metadata.gz: 1396c7927613825f5182660db63871bc06c114cc38ef334b6143929e7e88f730
4
- data.tar.gz: a6e2fcf2e65a3fa7d47e2a6f7f621d149e189f8be2ec980391e767ecc0000d27
3
+ metadata.gz: d18643889a68b9dab0c275a18940d220af312803ee0d7099d67c0b6a9444d32e
4
+ data.tar.gz: 13d363e73d4e0626054c1114fb3d4ca8c2e99cdce1801726322734b6fe4cd9e7
5
5
  SHA512:
6
- metadata.gz: 1d9e6b6a5394f54f846562e38053a490145f0c779aca7e61096ec43e3c2ee6055387639790352885b52db8a0918119469897e8b83fcef910965ad7e0d8f5e278
7
- data.tar.gz: ae5f360a2e89ede2df5c0f5d104105aeaefa10c0409b075074a903141d822690328a3e11ee7a55f8fb0256f632d72dac2f76f066b00440dd6cb89351c0c2d8f5
6
+ metadata.gz: 92b2a1264def50c34cb1ad8c970bc04d9b0c1784096e0afe1968923603158415ece5469f8242fa9a8968c0574a9e56586c24b71dcd1abc9059a2d85554eca1a2
7
+ data.tar.gz: cb67371f349e6e378fbbe93723ef7926db7bc49f5eb1a96fea0ea00a3af65c0a173aab1bf2aeb8b6772398d8ad7702c5058ff046a3015190f2fd7ad06f37e1e2
data/README.md CHANGED
@@ -8,8 +8,10 @@ A Ruby library for integrating with TapPay payment services. This gem provides a
8
8
 
9
9
  ## Features
10
10
 
11
- - Credit card payments (one-time and tokenized)
12
- - Instalment payments
11
+ - Multiple payment methods:
12
+ - Credit card payments (one-time and tokenized)
13
+ - Instalment payments (3 to 24 months)
14
+ - Line Pay
13
15
  - Refund processing
14
16
  - Transaction status queries
15
17
  - Comprehensive error handling
@@ -59,7 +61,10 @@ Tappay.configure do |config|
59
61
  # OR
60
62
  config.merchant_group_id = 'your_merchant_group_id'.freeze
61
63
 
64
+ # Payment-specific merchant IDs
62
65
  config.instalment_merchant_id = 'your_instalment_merchant_id'.freeze
66
+ config.line_pay_merchant_id = 'your_line_pay_merchant_id'.freeze
67
+
63
68
  config.currency = 'TWD'.freeze
64
69
  config.vat_number = 'your_vat_number'.freeze
65
70
  end
@@ -67,22 +72,44 @@ end
67
72
 
68
73
  ### Merchant ID Configuration
69
74
 
70
- The gem supports two types of merchant identification:
71
- 1. `merchant_id`: Individual merchant ID
72
- 2. `merchant_group_id`: Group merchant ID
75
+ The gem supports flexible merchant ID configuration:
76
+
77
+ 1. Global merchant ID:
78
+ - `merchant_id`: Default merchant ID for all payments
79
+ - `merchant_group_id`: Group merchant ID (mutually exclusive with merchant_id)
73
80
 
74
- Important rules:
75
- - You can set either `merchant_id` or `merchant_group_id` in the configuration, but not both
76
- - When making a payment, you can override the configured merchant ID by providing either `merchant_id` or `merchant_group_id` in the payment options
77
- - If you provide merchant ID in the payment options, it will take precedence over any configuration
81
+ 2. Payment-specific merchant IDs:
82
+ - `instalment_merchant_id`: Specific merchant ID for instalment payments
83
+ - `line_pay_merchant_id`: Specific merchant ID for Line Pay transactions
78
84
 
79
- Example of overriding merchant ID in payment:
85
+ Merchant ID Priority:
86
+ 1. Payment options merchant ID (if provided in the payment call)
87
+ 2. Payment-specific merchant ID (if configured)
88
+ 3. Global merchant ID
89
+
90
+ Example of merchant ID usage:
80
91
  ```ruby
81
- # Using merchant_group_id in payment options
92
+ # Using default merchant ID
93
+ result = Tappay::CreditCard::Pay.by_prime(
94
+ prime: 'prime_from_tappay_sdk',
95
+ amount: 100,
96
+ order_number: 'ORDER-123'
97
+ )
98
+
99
+ # Using payment-specific merchant ID
100
+ # This will automatically use line_pay_merchant_id if configured
101
+ result = Tappay::LinePay::Pay.new(
102
+ prime: 'line_pay_prime',
103
+ amount: 100,
104
+ frontend_redirect_url: 'https://example.com/line_pay/result',
105
+ backend_notify_url: 'https://example.com/line_pay/notify'
106
+ ).execute
107
+
108
+ # Overriding merchant ID in payment options
82
109
  result = Tappay::CreditCard::Pay.by_prime(
83
110
  prime: 'prime_from_tappay_sdk',
84
111
  amount: 100,
85
- merchant_group_id: 'group_123', # This will be used instead of configured merchant_id
112
+ merchant_id: 'override_merchant_id', # This takes highest priority
86
113
  order_number: 'ORDER-123'
87
114
  )
88
115
  ```
@@ -97,236 +124,60 @@ Tappay.configure do |config|
97
124
  config.partner_key = ENV['TAPPAY_PARTNER_KEY'].freeze
98
125
  config.app_id = ENV['TAPPAY_APP_ID'].freeze
99
126
  config.merchant_id = ENV['TAPPAY_MERCHANT_ID'].freeze
127
+ config.line_pay_merchant_id = ENV['TAPPAY_LINE_PAY_MERCHANT_ID'].freeze
128
+ config.instalment_merchant_id = ENV['TAPPAY_INSTALMENT_MERCHANT_ID'].freeze
100
129
  # ... other configurations
101
130
  end
102
131
  ```
103
132
 
104
- ### 3. Using Rails Credentials
105
-
106
- If you're using Rails, you can use credentials:
107
-
108
- ```ruby
109
- Tappay.configure do |config|
110
- config.mode = Rails.env.production? ? :production : :sandbox
111
- config.partner_key = Rails.application.credentials.tappay[:partner_key].freeze
112
- config.app_id = Rails.application.credentials.tappay[:app_id].freeze
113
- # ... other configurations
114
- end
115
- ```
116
-
117
- ## Environment-based Endpoints
118
-
119
- The gem automatically handles different endpoints for sandbox and production environments. You don't need to specify the full URLs - just set the mode:
120
-
121
- ```ruby
122
- # For sandbox (default)
123
- config.mode = :sandbox # Uses https://sandbox.tappaysdk.com/...
124
-
125
- # For production
126
- config.mode = :production # Uses https://prod.tappaysdk.com/...
127
- ```
128
-
129
- ## Card Holder Information
130
-
131
- You can provide cardholder information in two ways:
132
-
133
- ### 1. Using CardHolder Object (Recommended)
134
-
135
- ```ruby
136
- # Create a CardHolder object
137
- card_holder = Tappay::CardHolder.new(
138
- name: 'John Doe',
139
- email: 'john@example.com',
140
- phone_number: '+886923456789'
141
- )
142
-
143
- # Use in payment directly
144
- result = Tappay::CreditCard::Pay.by_prime(
145
- prime: 'prime_from_tappay_sdk',
146
- amount: 100,
147
- order_number: 'ORDER-123',
148
- card_holder: card_holder # No need to call as_json
149
- )
150
- ```
151
-
152
- ### 2. Using Hash Directly
153
-
154
- ```ruby
155
- result = Tappay::CreditCard::Pay.by_prime(
156
- prime: 'prime_from_tappay_sdk',
157
- amount: 100,
158
- order_number: 'ORDER-123',
159
- card_holder: {
160
- name: 'John Doe',
161
- email: 'john@example.com',
162
- phone_number: '+886923456789'
163
- }
164
- )
165
- ```
166
-
167
- Both approaches are valid and will work the same way. The CardHolder object provides a more structured way to handle cardholder information and includes validation.
168
-
169
- ## Payment URL
170
-
171
- When processing payments, the API response may include a `payment_url` field. This URL is used for redirecting users to complete their payment in scenarios such as:
172
-
173
- - 3D Secure verification
174
- - LINE Pay payment page
175
- - JKO Pay payment page
176
-
177
- Note: payment_url is not supported for:
178
- - Apple Pay
179
- - Google Pay
180
- - Samsung Pay
181
-
182
- Example of handling payment URL in response:
183
- ```ruby
184
- result = Tappay::CreditCard::Pay.by_prime(
185
- prime: 'prime_from_tappay_sdk',
186
- amount: 100,
187
- order_number: 'ORDER-123'
188
- )
189
-
190
- if result['status'] == 0
191
- if result['payment_url']
192
- # Redirect user to payment page for:
193
- # - 3D Secure verification
194
- # - LINE Pay payment
195
- # - JKO Pay payment
196
- redirect_to result['payment_url']
197
- end
198
- end
199
- ```
200
-
201
- ## URL Properties
202
-
203
- The `result_url` (JSONObject) property is required when using LINE Pay, JKOPAY, 悠遊付, Atome, Pi錢包, 全盈支付, or when three_domain_secure is true. It contains the following URL fields:
204
-
205
- ```json
206
- {
207
- "frontend_redirect_url": "https://example.com/redirect", // Required - URL where consumer will be redirected after completing the transaction
208
- "backend_notify_url": "https://example.com/notify", // Required - URL where your server receives transaction results (only port 443)
209
- "go_back_url": "https://example.com/back" // Optional - URL for 3D verification error cases (E.SUN, Cathay United, Taishin banks)
210
- }
211
- ```
212
-
213
- - `frontend_redirect_url` (String): After the consumer completes the transaction process in LINE Pay, JKOPAY, 悠遊付, Atome, Pi錢包, 全盈支付, or 3D verification, they will be redirected to this frontend URL. Must start with https.
214
-
215
- - `backend_notify_url` (String): URL where your server receives transaction results. Must start with https and only supports port 443.
216
-
217
- - `go_back_url` (String): For 3D verification transactions, this URL is used when consumers are redirected to the TapPay Error page due to improper operation. This scenario only occurs with E.SUN Bank, Cathay United Bank, and Taishin Bank. You can define this URL in the transaction request or set it in TapPay Portal > Developer Content > System Settings > Redirect Link Settings. It's strongly recommended to define this field for 3D transactions to ensure consumers can return to complete the transaction or view results.
218
-
219
133
  ## Usage
220
134
 
221
- ### Pay by Prime
222
-
223
- Use this method when the customer wants to pay with their credit card without storing the card information. The customer will need to enter their card information for each transaction.
135
+ ### Credit Card Payment
224
136
 
225
137
  ```ruby
226
- # Basic payment with prime
138
+ # One-time payment
227
139
  result = Tappay::CreditCard::Pay.by_prime(
228
140
  prime: 'prime_from_tappay_sdk',
229
- amount: 100,
230
- order_number: 'ORDER-123',
231
- currency: 'TWD',
232
- three_domain_secure: true, # Enable 3D secure if needed
233
- remember: true, # Set to true if you want to store the card for future payments
234
- card_holder: card_holder # Optional cardholder information
235
- )
236
-
237
- if result['status'] == 0
238
- # Payment successful
239
- transaction_id = result['rec_trade_id']
240
- if result['card_secret']
241
- # If remember is true, you'll get these tokens
242
- card_key = result['card_secret']['card_key']
243
- card_token = result['card_secret']['card_token']
244
- # Store card_key and card_token securely for future payments
245
- end
246
-
247
- # Handle payment URL if present (for 3D Secure, LINE Pay, JKO Pay)
248
- if result['payment_url']
249
- redirect_to result['payment_url']
250
- end
251
- end
252
- ```
253
-
254
- ### Pay by Token
255
-
256
- Use this method when the customer has opted to save their card information for future purchases. This provides a more convenient checkout experience as customers don't need to re-enter their card information.
257
-
258
- ```ruby
259
- # Recurring payment with stored card token
260
- result = Tappay::CreditCard::Pay.by_token(
261
- card_key: 'stored_card_key',
262
- card_token: 'stored_card_token',
263
- amount: 100,
264
- order_number: 'ORDER-124',
265
- currency: 'TWD',
266
- three_domain_secure: true, # Enable 3D secure if needed
267
- card_holder: card_holder # Optional cardholder information
141
+ amount: 1000,
142
+ details: 'Order Details',
143
+ cardholder: {
144
+ phone_number: '0912345678',
145
+ name: 'Test User',
146
+ email: 'test@example.com'
147
+ }
268
148
  )
269
149
 
270
- if result['status'] == 0
271
- # Payment successful
272
- transaction_id = result['rec_trade_id']
273
- end
274
- ```
275
-
276
- ### Instalment Payment
277
-
278
- ```ruby
279
- payment = Tappay::CreditCard::Instalment.new(
150
+ # Instalment payment (3-30 months)
151
+ result = Tappay::CreditCard::Instalment.by_prime(
280
152
  prime: 'prime_from_tappay_sdk',
281
- amount: 10000,
282
- instalment: 6, # 6 monthly installments
283
- details: 'Product description',
153
+ amount: 1000,
154
+ instalment: 12, # 12 months instalment
155
+ details: 'Order Details',
284
156
  cardholder: {
285
- phone_number: '+886923456789',
286
- name: 'John Doe',
287
- email: 'john@example.com'
157
+ phone_number: '0912345678',
158
+ name: 'Test User',
159
+ email: 'test@example.com'
288
160
  }
289
161
  )
290
-
291
- begin
292
- result = payment.execute
293
- if result['status'] == 0
294
- # Payment successful
295
- instalment_info = result['instalment_info']
296
- number_of_instalments = instalment_info['number_of_instalments']
297
- first_payment = instalment_info['first_payment']
298
- each_payment = instalment_info['each_payment']
299
-
300
- # Handle payment URL if present (for 3D Secure)
301
- if result['payment_url']
302
- redirect_to result['payment_url']
303
- end
304
- end
305
- rescue Tappay::PaymentError => e
306
- # Handle payment error
307
- end
308
162
  ```
309
163
 
310
- ### Refund Processing
164
+ ### Line Pay
311
165
 
312
166
  ```ruby
313
- refund = Tappay::CreditCard::Refund.new(
314
- transaction_id: 'transaction_id_from_payment',
315
- amount: 100
316
- )
317
-
318
- begin
319
- result = refund.execute
320
- if result['status'] == 0
321
- # Refund successful
322
- end
323
- rescue Tappay::RefundError => e
324
- # Handle refund error
325
- end
167
+ # Create Line Pay payment
168
+ result = Tappay::LinePay::Pay.new(
169
+ prime: 'line_pay_prime',
170
+ amount: 1000,
171
+ details: 'Order Details',
172
+ frontend_redirect_url: 'https://example.com/line_pay/result',
173
+ backend_notify_url: 'https://example.com/line_pay/notify'
174
+ ).execute
326
175
  ```
327
176
 
328
177
  ### Error Handling
329
178
 
179
+ The gem provides comprehensive error handling:
180
+
330
181
  ```ruby
331
182
  begin
332
183
  result = Tappay::CreditCard::Pay.by_prime(
@@ -334,48 +185,21 @@ begin
334
185
  amount: 100,
335
186
  order_number: 'ORDER-123'
336
187
  )
337
- rescue Tappay::PaymentError => e
338
- # Handle payment error
339
- puts e.message
340
188
  rescue Tappay::ValidationError => e
341
- # Handle validation error
342
- puts e.message
189
+ # Handle validation errors (e.g., missing required fields)
190
+ puts "Validation error: #{e.message}"
191
+ rescue Tappay::APIError => e
192
+ # Handle API errors (e.g., invalid prime, insufficient balance)
193
+ puts "API error: #{e.message}"
194
+ rescue Tappay::Error => e
195
+ # Handle other TapPay errors
196
+ puts "TapPay error: #{e.message}"
343
197
  end
344
198
  ```
345
199
 
346
- ### Example Usage with result_url
347
-
348
- ```ruby
349
- # Example of payment with result_url for LINE Pay
350
- result = Tappay::LinePay::Pay.create(
351
- prime: 'prime_from_tappay_sdk',
352
- amount: 100,
353
- merchant_id: 'your_merchant_id',
354
- details: 'Product Item',
355
- result_url: {
356
- frontend_redirect_url: 'https://example.com/payment/complete',
357
- backend_notify_url: 'https://example.com/payment/notify',
358
- go_back_url: 'https://example.com/payment/error'
359
- }
360
- )
361
-
362
- # Example of payment with result_url for 3D verification
363
- result = Tappay::CreditCard::Pay.by_prime(
364
- prime: 'prime_from_tappay_sdk',
365
- amount: 100,
366
- merchant_id: 'your_merchant_id',
367
- three_domain_secure: true,
368
- result_url: {
369
- frontend_redirect_url: 'https://example.com/3d/complete',
370
- backend_notify_url: 'https://example.com/3d/notify',
371
- go_back_url: 'https://example.com/3d/error'
372
- }
373
- )
374
- ```
375
-
376
200
  ## Development
377
201
 
378
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests.
202
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
379
203
 
380
204
  ## Contributing
381
205
 
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Tappay
2
4
  class Configuration
3
- attr_accessor :partner_key, :merchant_id, :merchant_group_id, :instalment_merchant_id, :app_id, :currency, :vat_number
5
+ attr_accessor :partner_key, :merchant_id, :merchant_group_id, :instalment_merchant_id,
6
+ :line_pay_merchant_id, :app_id, :currency, :vat_number
4
7
  attr_writer :api_version
5
8
 
6
9
  def initialize
@@ -23,7 +23,7 @@ module Tappay
23
23
  end
24
24
 
25
25
  def endpoint_url
26
- Tappay::Endpoints::CreditCard.payment_by_prime_url
26
+ Tappay::Endpoints::Payment.pay_by_prime_url
27
27
  end
28
28
 
29
29
  def validate_options!
@@ -33,6 +33,10 @@ module Tappay
33
33
 
34
34
  private
35
35
 
36
+ def get_merchant_id
37
+ Tappay.configuration.instalment_merchant_id || super
38
+ end
39
+
36
40
  def additional_required_options
37
41
  [:prime, :cardholder, :instalment]
38
42
  end
@@ -60,7 +64,7 @@ module Tappay
60
64
  end
61
65
 
62
66
  def endpoint_url
63
- Tappay::Endpoints::CreditCard.payment_by_token_url
67
+ Tappay::Endpoints::Payment.pay_by_token_url
64
68
  end
65
69
 
66
70
  def validate_options!
@@ -70,6 +74,10 @@ module Tappay
70
74
 
71
75
  private
72
76
 
77
+ def get_merchant_id
78
+ Tappay.configuration.instalment_merchant_id || super
79
+ end
80
+
73
81
  def additional_required_options
74
82
  [:card_key, :card_token, :instalment]
75
83
  end
@@ -1,117 +1,9 @@
1
- module Tappay
2
- module CreditCard
3
- class PaymentBase < Client
4
- def initialize(options = {})
5
- super
6
- validate_options!
7
- end
8
-
9
- def execute
10
- post(endpoint_url, payment_data)
11
- end
1
+ # frozen_string_literal: true
12
2
 
13
- protected
14
-
15
- def endpoint_url
16
- raise NotImplementedError, "Subclass must implement abstract method 'endpoint_url'"
17
- end
18
-
19
- def payment_data
20
- # Check configuration conflicts first
21
- if Tappay.configuration.merchant_group_id && Tappay.configuration.merchant_id
22
- raise Tappay::ValidationError, "merchant_group_id and merchant_id cannot be used together"
23
- end
24
-
25
- # Get values from options
26
- opt_group_id = options[:merchant_group_id]
27
- opt_merchant_id = options[:merchant_id]
28
-
29
- # Check for conflicts in options
30
- if opt_group_id && opt_merchant_id
31
- raise Tappay::ValidationError, "merchant_group_id and merchant_id cannot be used together"
32
- end
33
-
34
- # If options has any ID, use it exclusively
35
- if opt_group_id || opt_merchant_id
36
- merchant_group_id = opt_group_id
37
- merchant_id = opt_merchant_id
38
- else
39
- # If no options, use configuration
40
- merchant_group_id = Tappay.configuration.merchant_group_id
41
- merchant_id = Tappay.configuration.merchant_id
42
- end
43
-
44
- # Check if at least one is provided
45
- unless merchant_group_id || merchant_id
46
- raise Tappay::ValidationError, "Either merchant_group_id or merchant_id must be provided"
47
- end
48
-
49
- {
50
- partner_key: Tappay.configuration.partner_key,
51
- amount: options[:amount],
52
- details: options[:details],
53
- currency: options[:currency] || 'TWD',
54
- order_number: options[:order_number],
55
- three_domain_secure: options[:three_domain_secure] || false
56
- }.tap do |data|
57
- if merchant_group_id
58
- data[:merchant_group_id] = merchant_group_id
59
- else
60
- data[:merchant_id] = merchant_id
61
- end
62
- data[:cardholder] = card_holder_data if options[:cardholder]
63
- data[:instalment] = options[:instalment] if options[:instalment]
64
- data[:result_url] = options[:result_url] if options[:result_url]
65
- end
66
- end
67
-
68
- def card_holder_data
69
- return nil unless options[:cardholder]
70
-
71
- case options[:cardholder]
72
- when CardHolder
73
- options[:cardholder].to_h
74
- when Hash
75
- options[:cardholder]
76
- else
77
- raise ValidationError, "Invalid cardholder format"
78
- end
79
- end
80
-
81
- def validate_options!
82
- required = base_required_options + additional_required_options
83
- missing = required.select { |key| options[key].nil? }
84
- raise ValidationError, "Missing required options: #{missing.join(', ')}" if missing.any?
85
-
86
- validate_instalment! if options[:instalment]
87
- validate_result_url! if options[:three_domain_secure]
88
- end
89
-
90
- private
91
-
92
- def base_required_options
93
- [:amount, :details]
94
- end
95
-
96
- def additional_required_options
97
- []
98
- end
99
-
100
- def validate_instalment!
101
- unless options[:instalment].to_i.between?(1, 12)
102
- raise ValidationError, "Invalid instalment value. Must be between 1 and 12"
103
- end
104
- end
105
-
106
- def validate_result_url!
107
- return if options[:result_url] &&
108
- options[:result_url][:frontend_redirect_url] &&
109
- options[:result_url][:backend_notify_url]
110
-
111
- raise ValidationError, "result_url with frontend_redirect_url and backend_notify_url is required when three_domain_secure is true"
112
- end
113
- end
3
+ require 'tappay/payment_base'
114
4
 
5
+ module Tappay
6
+ module CreditCard
115
7
  class Pay < PaymentBase
116
8
  def self.by_prime(options = {})
117
9
  PayByPrime.new(options)
@@ -131,7 +23,7 @@ module Tappay
131
23
  end
132
24
 
133
25
  def endpoint_url
134
- Tappay::Endpoints::CreditCard.payment_by_prime_url
26
+ Tappay::Endpoints::Payment.pay_by_prime_url
135
27
  end
136
28
 
137
29
  private
@@ -150,7 +42,7 @@ module Tappay
150
42
  end
151
43
 
152
44
  def endpoint_url
153
- Tappay::Endpoints::CreditCard.payment_by_token_url
45
+ Tappay::Endpoints::Payment.pay_by_token_url
154
46
  end
155
47
 
156
48
  private
@@ -12,16 +12,20 @@ module Tappay
12
12
  end
13
13
  end
14
14
 
15
- module CreditCard
15
+ module Payment
16
16
  class << self
17
- def payment_by_prime_url
17
+ def pay_by_prime_url
18
18
  "#{Endpoints.base_url}/tpc/payment/pay-by-prime"
19
19
  end
20
20
 
21
- def payment_by_token_url
21
+ def pay_by_token_url
22
22
  "#{Endpoints.base_url}/tpc/payment/pay-by-token"
23
23
  end
24
+ end
25
+ end
24
26
 
27
+ module CreditCard
28
+ class << self
25
29
  def refund_url
26
30
  "#{Endpoints.base_url}/tpc/transaction/refund"
27
31
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tappay/payment_base'
4
+
5
+ module Tappay
6
+ module LinePay
7
+ class Pay < PaymentBase
8
+ def payment_data
9
+ super.merge(
10
+ prime: options[:prime],
11
+ result_url: {
12
+ frontend_redirect_url: options[:frontend_redirect_url],
13
+ backend_notify_url: options[:backend_notify_url]
14
+ },
15
+ remember: options[:remember] || false
16
+ )
17
+ end
18
+
19
+ def endpoint_url
20
+ Tappay::Endpoints::Payment.pay_by_prime_url
21
+ end
22
+
23
+ private
24
+
25
+ def get_merchant_id
26
+ Tappay.configuration.line_pay_merchant_id || super
27
+ end
28
+
29
+ def additional_required_options
30
+ [:prime, :frontend_redirect_url, :backend_notify_url]
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tappay
4
+ class PaymentBase < Client
5
+ VALID_INSTALMENT_VALUES = [0, 3, 6, 12, 24, 30].freeze
6
+
7
+ def initialize(options = {})
8
+ super
9
+ validate_options!
10
+ end
11
+
12
+ def execute
13
+ post(endpoint_url, payment_data)
14
+ end
15
+
16
+ protected
17
+
18
+ def endpoint_url
19
+ raise NotImplementedError, "Subclass must implement abstract method 'endpoint_url'"
20
+ end
21
+
22
+ def payment_data
23
+ # Check configuration conflicts first
24
+ if Tappay.configuration.merchant_group_id && Tappay.configuration.merchant_id
25
+ raise Tappay::ValidationError, "merchant_group_id and merchant_id cannot be used together"
26
+ end
27
+
28
+ # Get values from options
29
+ opt_group_id = options[:merchant_group_id]
30
+ opt_merchant_id = options[:merchant_id]
31
+
32
+ # Check for conflicts in options
33
+ if opt_group_id && opt_merchant_id
34
+ raise Tappay::ValidationError, "merchant_group_id and merchant_id cannot be used together"
35
+ end
36
+
37
+ # If options has any ID, use it exclusively
38
+ if opt_group_id || opt_merchant_id
39
+ merchant_group_id = opt_group_id
40
+ merchant_id = opt_merchant_id
41
+ else
42
+ # If no options, use configuration
43
+ merchant_group_id = Tappay.configuration.merchant_group_id
44
+ merchant_id = get_merchant_id
45
+ end
46
+
47
+ # Check if at least one is provided
48
+ unless merchant_group_id || merchant_id
49
+ raise Tappay::ValidationError, "Either merchant_group_id or merchant_id must be provided"
50
+ end
51
+
52
+ {
53
+ partner_key: Tappay.configuration.partner_key,
54
+ amount: options[:amount],
55
+ details: options[:details],
56
+ currency: options[:currency] || 'TWD',
57
+ order_number: options[:order_number],
58
+ three_domain_secure: options[:three_domain_secure] || false
59
+ }.tap do |data|
60
+ if merchant_group_id
61
+ data[:merchant_group_id] = merchant_group_id
62
+ else
63
+ data[:merchant_id] = merchant_id
64
+ end
65
+ data[:cardholder] = card_holder_data if options[:cardholder]
66
+ data[:result_url] = options[:result_url] if options[:result_url]
67
+ data[:instalment] = options[:instalment] || 0
68
+ end
69
+ end
70
+
71
+ def card_holder_data
72
+ return nil unless options[:cardholder]
73
+
74
+ case options[:cardholder]
75
+ when CardHolder
76
+ options[:cardholder].to_h
77
+ when Hash
78
+ options[:cardholder]
79
+ else
80
+ raise ValidationError, "Invalid cardholder format"
81
+ end
82
+ end
83
+
84
+ def validate_options!
85
+ required = base_required_options + additional_required_options
86
+ missing = required.select { |key| options[key].nil? }
87
+ raise ValidationError, "Missing required options: #{missing.join(', ')}" if missing.any?
88
+
89
+ validate_amount!
90
+ validate_instalment! if options[:instalment]
91
+ validate_result_url! if options[:three_domain_secure]
92
+ end
93
+
94
+ private
95
+
96
+ def validate_amount!
97
+ amount = options[:amount]
98
+ if !amount.is_a?(Numeric)
99
+ raise ValidationError, "amount must be a number"
100
+ elsif amount <= 0
101
+ raise ValidationError, "amount must be greater than 0"
102
+ end
103
+ end
104
+
105
+ def get_merchant_id
106
+ Tappay.configuration.merchant_id
107
+ end
108
+
109
+ def base_required_options
110
+ [:amount, :details]
111
+ end
112
+
113
+ def additional_required_options
114
+ []
115
+ end
116
+
117
+ def validate_instalment!
118
+ instalment = options[:instalment].to_i
119
+ unless VALID_INSTALMENT_VALUES.include?(instalment)
120
+ raise ValidationError, "Instalment must be one of: #{VALID_INSTALMENT_VALUES.join(', ')}"
121
+ end
122
+ end
123
+
124
+ def validate_result_url!
125
+ result_url = options[:result_url]
126
+ raise ValidationError, "result_url must be a hash" unless result_url.is_a?(Hash)
127
+
128
+ required_fields = %w[frontend_redirect_url backend_notify_url]
129
+ missing = required_fields.select { |field| result_url[field.to_sym].nil? && result_url[field].nil? }
130
+
131
+ if missing.any?
132
+ raise ValidationError, "result_url must contain both frontend_redirect_url and backend_notify_url"
133
+ end
134
+ end
135
+ end
136
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Tappay
2
- VERSION = "0.6.0"
4
+ VERSION = "0.8.0"
3
5
  end
data/lib/tappay.rb CHANGED
@@ -10,6 +10,7 @@ require_relative "tappay/transaction/query"
10
10
  require_relative "tappay/credit_card/pay"
11
11
  require_relative "tappay/credit_card/refund"
12
12
  require_relative "tappay/credit_card/instalment"
13
+ require_relative "tappay/line_pay/pay"
13
14
 
14
15
  module Tappay
15
16
  class Error < StandardError; end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tappay_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zac
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2024-12-30 00:00:00.000000000 Z
10
+ date: 2025-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: httparty
@@ -83,6 +83,8 @@ files:
83
83
  - lib/tappay/credit_card/refund.rb
84
84
  - lib/tappay/endpoints.rb
85
85
  - lib/tappay/errors.rb
86
+ - lib/tappay/line_pay/pay.rb
87
+ - lib/tappay/payment_base.rb
86
88
  - lib/tappay/transaction/query.rb
87
89
  - lib/tappay/version.rb
88
90
  - lib/tappay_ruby.rb