global_collect 0.1.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 (90) hide show
  1. data/COPYING +11 -0
  2. data/README.markdown +109 -0
  3. data/Rakefile +34 -0
  4. data/VERSION +1 -0
  5. data/examples/cancel_payment.rb +14 -0
  6. data/examples/convert_amount.rb +14 -0
  7. data/examples/get_order_status.rb +15 -0
  8. data/examples/insert_order_with_payment.rb +37 -0
  9. data/examples/process_challenged.rb +13 -0
  10. data/examples/set_payment.rb +23 -0
  11. data/examples/test_connection.rb +13 -0
  12. data/global_collect.gemspec +170 -0
  13. data/lib/global_collect/api_client.rb +71 -0
  14. data/lib/global_collect/builders/do_refund/credit_card_payment.rb +12 -0
  15. data/lib/global_collect/builders/do_refund/payment.rb +39 -0
  16. data/lib/global_collect/builders/insert_order_with_payment/credit_card_online_payment.rb +38 -0
  17. data/lib/global_collect/builders/insert_order_with_payment/hosted_credit_card_online_payment.rb +4 -0
  18. data/lib/global_collect/builders/insert_order_with_payment/order.rb +75 -0
  19. data/lib/global_collect/builders/insert_order_with_payment/payment.rb +25 -0
  20. data/lib/global_collect/builders/set_payment/payment.rb +23 -0
  21. data/lib/global_collect/const/payment_product.rb +38 -0
  22. data/lib/global_collect/const/payment_status.rb +90 -0
  23. data/lib/global_collect/field_validator.rb +47 -0
  24. data/lib/global_collect/request_models/base.rb +37 -0
  25. data/lib/global_collect/request_models/do_refund/credit_card_payment.rb +12 -0
  26. data/lib/global_collect/request_models/do_refund/payment.rb +39 -0
  27. data/lib/global_collect/request_models/insert_order_with_payment/credit_card_online_payment.rb +44 -0
  28. data/lib/global_collect/request_models/insert_order_with_payment/hosted_credit_card_online_payment.rb +19 -0
  29. data/lib/global_collect/request_models/insert_order_with_payment/order.rb +67 -0
  30. data/lib/global_collect/request_models/insert_order_with_payment/payment.rb +17 -0
  31. data/lib/global_collect/request_models/set_payment/payment.rb +17 -0
  32. data/lib/global_collect/requests/base.rb +43 -0
  33. data/lib/global_collect/requests/cancel_payment.rb +22 -0
  34. data/lib/global_collect/requests/composite.rb +23 -0
  35. data/lib/global_collect/requests/convert_amount.rb +29 -0
  36. data/lib/global_collect/requests/do_refund.rb +8 -0
  37. data/lib/global_collect/requests/get_order_status.rb +24 -0
  38. data/lib/global_collect/requests/insert_order_with_payment.rb +10 -0
  39. data/lib/global_collect/requests/process_challenged.rb +22 -0
  40. data/lib/global_collect/requests/set_payment.rb +9 -0
  41. data/lib/global_collect/requests/simple.rb +39 -0
  42. data/lib/global_collect/requests/test_connection.rb +8 -0
  43. data/lib/global_collect/responses/base.rb +73 -0
  44. data/lib/global_collect/responses/convert_amount/response_methods.rb +8 -0
  45. data/lib/global_collect/responses/do_refund/response_methods.rb +24 -0
  46. data/lib/global_collect/responses/get_order_status/v1_response_methods.rb +49 -0
  47. data/lib/global_collect/responses/get_order_status/v2_response_methods.rb +67 -0
  48. data/lib/global_collect/responses/insert_order_with_payment/credit_card_online_payment_response_methods.rb +31 -0
  49. data/lib/global_collect/responses/insert_order_with_payment/hosted_merchant_link_payment_response_methods.rb +16 -0
  50. data/lib/global_collect/responses/success_row.rb +15 -0
  51. data/lib/global_collect.rb +81 -0
  52. data/spec/api_client_spec.rb +93 -0
  53. data/spec/builders/do_refund/credit_card_payment_spec.rb +32 -0
  54. data/spec/builders/do_refund/payment_spec.rb +51 -0
  55. data/spec/builders/insert_order_with_payment/credit_card_online_payment_spec.rb +59 -0
  56. data/spec/builders/insert_order_with_payment/hosted_credit_card_online_payment_spec.rb +59 -0
  57. data/spec/builders/insert_order_with_payment/order_spec.rb +88 -0
  58. data/spec/builders/insert_order_with_payment/payment_spec.rb +37 -0
  59. data/spec/builders/set_payment/payment_spec.rb +35 -0
  60. data/spec/field_validator_spec.rb +79 -0
  61. data/spec/global_collect_spec.rb +43 -0
  62. data/spec/request_models/base_spec.rb +31 -0
  63. data/spec/request_models/insert_order_with_payment/credit_card_online_payment_spec.rb +11 -0
  64. data/spec/request_models/insert_order_with_payment/hosted_credit_card_online_payment_spec.rb +38 -0
  65. data/spec/requests/base_spec.rb +40 -0
  66. data/spec/requests/composite_spec.rb +48 -0
  67. data/spec/requests/convert_amount.rb +34 -0
  68. data/spec/requests/insert_order_with_payment_spec.rb +60 -0
  69. data/spec/requests/simple_spec.rb +37 -0
  70. data/spec/responses/base_spec.rb +59 -0
  71. data/spec/responses/convert_amount/response_methods_spec.rb +13 -0
  72. data/spec/responses/do_refund/response_methods_spec.rb +15 -0
  73. data/spec/responses/get_order_status/v1_response_methods_spec.rb +28 -0
  74. data/spec/responses/get_order_status/v2_response_methods_spec.rb +28 -0
  75. data/spec/responses/insert_order_with_payment/credit_card_online_payment_response_methods_spec.rb +13 -0
  76. data/spec/responses/insert_order_with_payment/hosted_merchant_link_payment_response_methods_spec.rb +13 -0
  77. data/spec/responses/succcess_row_spec.rb +26 -0
  78. data/spec/spec_helper.rb +130 -0
  79. data/spec/support/challenged_iowp_response.xml +51 -0
  80. data/spec/support/successful_convert_amount_response.xml +29 -0
  81. data/spec/support/successful_do_refund_response.xml +36 -0
  82. data/spec/support/successful_get_order_status_v1_response.xml +72 -0
  83. data/spec/support/successful_get_order_status_v2_response.xml +50 -0
  84. data/spec/support/successful_hosted_iowp_response.xml +60 -0
  85. data/spec/support/successful_iowp_response.xml +49 -0
  86. data/spec/support/successful_process_challenged_response.xml +26 -0
  87. data/spec/support/unsuccessful_do_refund_response.xml +29 -0
  88. data/spec/support/unsuccessful_iowp_response.xml +42 -0
  89. data/spec/support/unsuccessful_process_challenged_response.xml +30 -0
  90. metadata +213 -0
@@ -0,0 +1,71 @@
1
+ module GlobalCollect
2
+ class ApiClient
3
+ include HTTParty
4
+ # WDL §4 specifies the body should be xml
5
+ format :xml
6
+ # WDL §3.1 specifies the content type
7
+ headers "Content-Type" => "text/xml; charset=utf-8"
8
+ # WDL §3.6 recommends this timeout
9
+ DEFAULT_TIMEOUT = 70
10
+ # Net::HTTP warns that debug_output should not be set in production
11
+ # because it is a security problem.
12
+ debug_output(nil)
13
+
14
+
15
+ attr_reader :service, :environment, :authentication
16
+ def initialize(service, environment, authentication)
17
+ @serivce = service
18
+ @environment = environment
19
+ @authentication = authentication
20
+ @service_url = ApiClient.service_url(service, environment, authentication)
21
+ end
22
+
23
+ def make_request(request, add_mixins=true)
24
+ xml = request.to_xml
25
+ GlobalCollect.wire_logger.info("POST [#{request.action} v#{request.version}] => [#{@service_url}] - body:\n#{xml}")
26
+
27
+ response = nil
28
+ request_time = Benchmark.realtime do
29
+ response = self.class.post(@service_url,
30
+ :body => xml,
31
+ :timeout => DEFAULT_TIMEOUT
32
+ )
33
+ end
34
+
35
+ unless response
36
+ error_message = "Request [#{request.action} v#{request.version}] => [#{@service_url}] failed! No response!"
37
+ GlobalCollect.wire_logger.error(error_message)
38
+ raise error_message
39
+ end
40
+ GlobalCollect.wire_logger.info("RESP [#{request.action} v#{request.version}] => #{response.code} - #{request_time} s - body:\n#{response.body}")
41
+
42
+ base = GlobalCollect::Responses::Base.new(response.delegate, response.body)
43
+ raise "Malformed response to #{request.action} request! Body: '#{response.body}'" if base.malformed?
44
+ request.suggested_response_mixins.each{|m| base.extend(m) } if add_mixins
45
+ base
46
+ end
47
+
48
+ private
49
+
50
+ def self.service_url(service, environment, authentication)
51
+ # WDL §§3.4-5 specify the allowed arguments
52
+ raise ArgumentError.new("Only [Hosted] Merchant Link is currently supported!") unless [:merchant_link].include?(service)
53
+ raise ArgumentError.new("Only :test and :production are valid environemnts!") unless [:test, :production].include?(environment)
54
+ raise ArgumentError.new("Only :ip_check and :client_auth are valid authentication schemes!") unless [:ip_check, :client_auth].include?(authentication)
55
+ {
56
+ :merchant_link => {
57
+ # WDL §3.4 specifies the test environment urls
58
+ :test => {
59
+ :ip_check => "https://ps.gcsip.nl/wdl/wdl",
60
+ :client_auth => "https://ca.gcsip.nl/wdl/wdl"
61
+ },
62
+ # WDL §3.5 specifies the prodution environment urls
63
+ :production => {
64
+ :ip_check => "https://ps.gcsip.com/wdl/wdl",
65
+ :client_auth => "https://ca.gcsip.com/wdl/wdl"
66
+ }
67
+ }
68
+ }[service][environment][authentication]
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,12 @@
1
+ module GlobalCollect::Builders::DoRefund
2
+ class CreditCardPayment < Payment
3
+ # WDL §5.16.1 defines the refund payment fields
4
+ def payment_fields
5
+ super + %w[
6
+ PAYMENTPRODUCTID
7
+ CREDITCARDNUMBER
8
+ EXPIRYDATE
9
+ ]
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,39 @@
1
+ module GlobalCollect::Builders::DoRefund
2
+ class Payment < Struct.new(:payment)
3
+ def build(node)
4
+ node.tag!("PAYMENT") do |payment_node|
5
+ payment_fields.each do |field|
6
+ payment_node.tag!(field, payment[field]) if payment[field]
7
+ end
8
+ end
9
+ end
10
+
11
+ # WDL §5.28 Table 105 specifies general payment fields
12
+ def payment_fields
13
+ %w[
14
+ ORDERID
15
+ EFFORTID
16
+ MERCHANTREFERENCE
17
+ REFERENCEORIGPAYMENT
18
+ CURRENCYCODE
19
+ AMOUNT
20
+ COUNTRYCODE
21
+ REFUNDDATE
22
+ SURNAME
23
+ FIRSTNAME
24
+ PREFIXSURNAME
25
+ TITLE
26
+ COMPANYNAME
27
+ COMPANYDATA
28
+ STREET
29
+ HOUSENUMBER
30
+ ADDITIONALADDRESSINFO
31
+ ZIP
32
+ CITY
33
+ STATE
34
+ EMAILADDRESS
35
+ EMAILTYPEINDICATOR
36
+ ]
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,38 @@
1
+ module GlobalCollect::Builders::InsertOrderWithPayment
2
+ class CreditCardOnlinePayment < Payment
3
+ # WDL §5.28 Table 106 specifies credit card payment fields
4
+ def payment_fields
5
+ super + %w[
6
+ EXPIRYDATE
7
+ CREDITCARDNUMBER
8
+ ISSUENUMBER
9
+ CVV
10
+ CVVINDICATOR
11
+ AVSINDICATOR
12
+ AUTHENTICATIONINDICATOR
13
+ STTINDICATOR
14
+ FIRSTNAME
15
+ PREFIXSURNAME
16
+ SURNAME
17
+ STREET
18
+ HOUSENUMBER
19
+ CUSTOMERIPADDRESS
20
+ ADDITIONALADDRESSINFO
21
+ ZIP
22
+ CITY
23
+ STATE
24
+ PHONENUMBER
25
+ EMAIL
26
+ BIRTHDATE
27
+ DCCINDICATOR
28
+ ISSUERAMOUNT
29
+ ISSUERCURRENCYCODE
30
+ MARGINRATEPERCENTAGE
31
+ EXCHANGERATESOURCENAME
32
+ EXCHANGERATE
33
+ EXCHANGERATEVALIDTO
34
+ MAC
35
+ ]
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,4 @@
1
+ module GlobalCollect::Builders::InsertOrderWithPayment
2
+ class HostedCreditCardOnlinePayment < CreditCardOnlinePayment
3
+ end
4
+ end
@@ -0,0 +1,75 @@
1
+ module GlobalCollect::Builders::InsertOrderWithPayment
2
+ class Order < Struct.new(:order)
3
+ def build(node)
4
+ node.tag!("ORDER") do |order_node|
5
+ order_fields.each do |field|
6
+ order_node.tag!(field, order[field]) if order[field]
7
+ end
8
+ end
9
+ end
10
+
11
+ # WDL §5.28.1 First table specifies the full list of possible fields
12
+ def order_fields
13
+ %w[
14
+ ORDERID
15
+ ORDERTYPE
16
+ AMOUNT
17
+ AMOUNTSIGN
18
+ CURRENCYCODE
19
+ LANGUAGECODE
20
+ COUNTRYCODE
21
+ OVERWRITEPAYMENTREFERNCE
22
+ IPADDRESSCUSTOMER
23
+ CUSTOMERID
24
+ MANDATE
25
+ TITLE
26
+ FIRSTNAME
27
+ PREFIXSURNAME
28
+ SURNAME
29
+ STREET
30
+ HOUSENUMBER
31
+ ADDITIONALADDRESSINFO
32
+ ZIP
33
+ CITY
34
+ STATE
35
+ SHIPPINGTITLE
36
+ SHIPPINGFIRSTNAME
37
+ SHIPPINGPREFIXSURNAME
38
+ SHIPPINGSURNAME
39
+ SHIPPINGSTREET
40
+ SHIPPINGHOUSENUMBER
41
+ SHIPPINGADDITIONALADDRESSINFO
42
+ SHIPPINGZIP
43
+ SHIPPINGCITY
44
+ SHIPPINGSTATE
45
+ SHIPPINGCOUNTRYCODE
46
+ MERCHANTREFERENCE
47
+ DESCRIPTOR
48
+ RESELLERID
49
+ EMAIL
50
+ EMAILTYPEINDICATOR
51
+ COMPANYNAME
52
+ COMPANYDATA
53
+ SEX
54
+ VATNUMBER
55
+ PHONENUMBER
56
+ FAXNUMBER
57
+ INVOICENUMBER
58
+ INVOICETYPE
59
+ INVOICEDATE
60
+ INVOICECLASS
61
+ ORDERDATE
62
+ BIRTHDATE
63
+ TEXTQUALIFIER1
64
+ TEXTQUALIFIER2
65
+ TEXTQUALIFIER3
66
+ ADDITIONALDATA
67
+ STARTDATE
68
+ ENDDATE
69
+ NUMBEROFPAYMENTS
70
+ STEPWEEK
71
+ STEPMONTH
72
+ ]
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,25 @@
1
+ module GlobalCollect::Builders::InsertOrderWithPayment
2
+ class Payment < Struct.new(:payment)
3
+ def build(node)
4
+ node.tag!("PAYMENT") do |payment_node|
5
+ payment_fields.each do |field|
6
+ payment_node.tag!(field, payment[field]) if payment[field]
7
+ end
8
+ end
9
+ end
10
+
11
+ # WDL §5.28 Table 105 specifies general payment fields
12
+ def payment_fields
13
+ %w[
14
+ PAYMENTPRODUCTID
15
+ AMOUNT
16
+ AMOUNTSIGN
17
+ CURRENCYCODE
18
+ LANGUAGECODE
19
+ COUNTRYCODE
20
+ HOSTEDINDICATOR
21
+ RETURNURL
22
+ ]
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ module GlobalCollect::Builders::SetPayment
2
+ class Payment < Struct.new(:payment)
3
+ def build(node)
4
+ node.tag!("PAYMENT") do |payment_node|
5
+ payment_fields.each do |field|
6
+ payment_node.tag!(field, payment[field]) if payment[field]
7
+ end
8
+ end
9
+ end
10
+
11
+ # WDL §5.33.1 specifies general payment fields
12
+ def payment_fields
13
+ %w[
14
+ ORDERID
15
+ EFFORTID
16
+ PAYMENTPRODUCTID
17
+ AMOUNT
18
+ CURRENCYCODE
19
+ DATECOLLECT
20
+ ]
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,38 @@
1
+ module GlobalCollect::Const
2
+ module PaymentProduct
3
+ def self.from_sym(sym)
4
+ info(sym)
5
+ end
6
+
7
+ def self.from_code(code)
8
+ sym = PRODUCTS.detect{|k,v| v.first == code.to_i }.first
9
+ from_sym(sym)
10
+ end
11
+
12
+ private
13
+ def self.info(sym)
14
+ raise ArgumentError.new("Invalid payment product symbol ''!") unless PRODUCTS[sym]
15
+ Product.new(sym, *PRODUCTS[sym])
16
+ end
17
+ class Product < Struct.new(:symbol, :code, :description)
18
+ def to_s
19
+ symbol.to_s
20
+ end
21
+ end
22
+
23
+ PRODUCTS = {
24
+ :visa => [1 , "Visa Online" ],
25
+ :amex => [2 , "American Express Online"],
26
+ :mc => [3 , "MasterCard Online" ],
27
+ :visa_delta => [111, "Visa Delta" ],
28
+ :maestro => [117, "Maestro" ],
29
+ :solo => [118, "Solo" ],
30
+ :visa_electron => [122, "Visa Electron" ],
31
+ :dankort => [123, "Dankort" ],
32
+ :laser => [124, "Laser" ],
33
+ :jcb => [125, "JCB" ],
34
+ :discover => [128, "Discover" ],
35
+ :carte_bleue => [130, "Carte Bleue Online" ]
36
+ }
37
+ end
38
+ end
@@ -0,0 +1,90 @@
1
+ module GlobalCollect::Const
2
+ module PaymentStatus
3
+ def self.from_code(code)
4
+ code = code.to_i
5
+ raise ArgumentError.new("Invalid payment status code!") unless STATUSES.key?(code)
6
+ Status.new(code, *STATUSES[code])
7
+ end
8
+
9
+ def self.from_name(name)
10
+ code, strings = STATUSES.detect{|k,v| v.first == name }
11
+ status(code) if code
12
+ end
13
+
14
+ private
15
+
16
+ STATUSES = {
17
+ 0 => ["CREATED" , "The payment attempt was created." ],
18
+ 20 => ["PENDING AT MERCHANT" , "The Hosted Merchant Link transaction is waiting for the consumer to be redirected by the merchant to WebCollect." ],
19
+ 25 => ["PENDING AT GLOBALCOLLECT" , "The Hosted Merchant Link transaction is waiting for the consumer to enter missing data on the payment pages of GlobalCollect." ],
20
+ 30 => ["PENDING AT GLOBALCOLLECT" , "The Hosted Merchant Link transaction is waiting for the consumer to be redirected by WebCollect to the payment pages of the bank (optionally after the completion of missing data)." ],
21
+ 50 => ["PENDING AT BANK (Real-time Bank Transfer)/ENROLLED (Credit Card Online)" , "The payment request and consumer have been forwarded to the payment pages of the bank./The payment request and consumer have been forwarded to the authentication pages of the card issuer." ],
22
+ 55 => ["PENDING AT CONSUMER" , "The consumer received all payment details to initiate the transaction. The consumer must go to the (bank) office to initiate the payment." ],
23
+ 60 => ["NOT ENROLLED" , "The consumer is not enrolled for 3D Secure authentications." ],
24
+ 65 => ["PENDING PAYMENT (CONSUMER AT BANK)" , "The consumer is at an office to initiate a transaction. The status isused when the supplier polls the WebCollect database to verify if apayment on an order is (still) possible." ],
25
+ 70 => ["INDOUBT AT BANK" , "The status of the payment is in doubt at the bank." ],
26
+ 100 => ["REJECTED" , "WebCollect rejected the payment instruction." ],
27
+ 120 => ["REJECTED BY BANK" , "The bank rejected the payment." ],
28
+ 125 => ["CANCELLED BY BANK" , "The consumer cancelled the payment while on the bank’s payment pages." ],
29
+ 130 => ["FAILED VERIFICATION" , "The payment has failed." ],
30
+ 140 => ["EXPIRED AT BANK" , "The payment was not completed within the given set time limit by the consumer and is expired. The payment has failed." ],
31
+ 150 => ["TIMED OUT AT BANK" , "WebCollect did not receive information regarding the outcome of the payment at the bank." ],
32
+ 160 => ["DENIED" , "The transaction had been rejected for reasons of suspected fraud." ],
33
+ 170 => ["AUTHORISATION EXPIRED" , "The authorisation is expired because no explicit settlement request was received in time." ],
34
+ 172 => ["AUTHENTICATION_ENROLLMENT_EXPIRED" , "The enrolment period was pending too long." ],
35
+ 175 => ["AUTHENTICATION_VALIDATION_EXPIRED" , "The validation period was pending too long." ],
36
+ 180 => ["INVALED PARES OR NOT COMPLETED" , "The cardholder authentication response from the bank was invalid or not completed." ],
37
+ 200 => ["CARDHOLDER AUTHENTICATED" , "The cardholder was successfully authenticated." ],
38
+ 220 => ["COULD NOT AUTHENTICATE" , "The authentication service was out of order, cardholder could not be authenticated." ],
39
+ 230 => ["CARDHOLDER NOT PARTICIPATING" , "The cardholder is not participating in the 3D Secure authentication program." ],
40
+ 280 => ["INVALED PARES OR NOT COMPLETED" , "The cardholder authentication response from the bank was invalid or not completed. Authorization not possible." ],
41
+ 300 => ["AUTHORISATION TESTED" , "Authorisation tested. This payment will be re-authorised and settled offline." ],
42
+ 310 => ["NOT ENROLLED" , "The consumer is not enrolled for 3D Secure authentications. Authorization not possible." ],
43
+ 320 => ["COULD NOT AUTHENTICATE" , "The authentication service was out of order, cardholder could not be authenticated. Authorization not possible." ],
44
+ 330 => ["CARDHOLDER NOT PARTICIPATING" , "The cardholder is not participating in the 3D Secure authentication program. Authorization not possible." ],
45
+ 350 => ["CARDHOLDER AUTHENTICATED" , "The cardholder was successfully authenticated. Authorization not possible." ],
46
+ 400 => ["REVISED" , "The consumer or WebCollect has revised the payment (with other payment product)." ],
47
+ 525 => ["CHALLENGED" , "The payment was challenged by your Fraud Ruleset and is pending. Use Process Challenged API or Web Payment Console if you choose to process further." ],
48
+ 550 => ["REFERRED" , "The payment was referred. A ‘manual’ authorisation attempt will be made shortly." ],
49
+ 600 => ["PENDING" , "The payment instruction is pending waiting for a mandate (direct debit), settlement (credit card online) or acceptation (recurringorders)." ],
50
+ 650 => ["PENDING VERIFICATION" , "The real-time bank payment is pending verification by the batch process. If followed by 50 PENDING AT BANK, the verificationcould not be carried out successfully." ],
51
+ 800 => ["READY" , "GlobalCollect accepted the payment instruction. For Credit Card Online the payment is authorized, but not yet settled. For a Real-time Bank Transfer the return message from the bank indicates that the payment was successful."],
52
+ 850 => ["MARKED FOR SENDING" , "Temporary status. The payment instruction was accepted and is being further processed." ],
53
+ 900 => ["SENT" , "Temporary status. The payment instruction was accepted and is being further processed." ],
54
+ 900 => ["PROCESSED" , "The refund was processed." ],
55
+ 950 => ["INVOICE_SENT" , "The invoice was printed and sent." ],
56
+ 975 => ["SETTLEMENT IN PROGRESS" , "The settlement file was sent for processing at the financiali nstitution." ],
57
+ 1000 => ["PAID" , "The payment was paid." ],
58
+ 1010 => ["ACCOUNT DEBITED" , "GlobalCollect debited the consumer account." ],
59
+ 1020 => ["CORRECTED" , "GlobalCollect corrected the payment information given." ],
60
+ 1030 => ["WITHDRAWN CHARGEBACK" , "The chargeback has been withdrawn." ],
61
+ 1050 => ["REMITTED" , "The funds have been made available for remittance to the merchant." ],
62
+ 1100 => ["REJECTED" , "GlobalCollect rejected the payment attempt." ],
63
+ 1110 => ["REFUSED BY ACCEPTING BANK" , "The acquiring bank rejected the direct debit." ],
64
+ 1120 => ["REFUSED SETTLEMENT" , "Refused settlement before payment from Acquirer." ],
65
+ 1150 => ["REFUSED SETTLEMENT" , "Refused settlement after payment from Acquirer" ],
66
+ 1210 => ["REFUSED BY CONSUMER BANK" , "The bank of the consumer rejected the direct debit." ],
67
+ 1250 => ["BOUNCED" , "The payment bounced." ],
68
+ 1500 => ["CHARGED BACK BY CONSUMER" , "The payment was charged back by the consumer." ],
69
+ 1510 => ["REVERSAL BY CONSUMER" , "The consumer reversed the direct debit payment." ],
70
+ 1520 => ["REVERSED" , "The payment was reversed." ],
71
+ 1800 => ["REFUNDED" , "The payment was refunded." ],
72
+ 1810 => ["CORRECTED REFUND" , "GlobalCollect corrected the refund information given." ],
73
+ 1850 => ["REFUSED REFUND" , "Refund is refused by the Acquirer" ],
74
+ 2000 => ["ACCOUNT CREDITED" , "GlobalCollect credited the consumer account." ],
75
+ 2030 => ["WITHDRAWN REVERSED PAYOUT" , "Withdrawn Reversed Payout" ],
76
+ 2110 => ["REJECTED BY GLOBALCOLLECT" , "GlobalCollect rejected the payout attempt." ],
77
+ 2120 => ["REFUSED BY ACCEPTING BANK" , "The acquiring bank rejected the payout attempt." ],
78
+ 2130 => ["REFUSED BY CONSUMER BANK" , "The consumer bank rejected the payout attempt." ],
79
+ 2210 => ["REVERSAL BY CONSUMER" , "The consumer reversed the payout." ],
80
+ 2220 => ["REVERSED" , "The payout was reversed." ],
81
+ 99999 => ["CANCELLED" , "Payment/Refund/Payout attempt was cancelled by the merchant." ]
82
+ }
83
+
84
+ class Status < Struct.new(:code, :name, :description)
85
+ def to_s
86
+ name
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,47 @@
1
+ module GlobalCollect
2
+ # WDL §5 TABLE 10 specifies the tokens and their meaning
3
+ class FieldValidator
4
+ def initialize(token, required)
5
+ if token =~ /([A-Z]{1,2})(\d+)?/
6
+ @type = $1
7
+ @size = $2.to_i
8
+ @required = (required == "R")
9
+ else
10
+ raise ArgumentError.new("Invalid validation token '#{token}'!")
11
+ end
12
+ end
13
+
14
+ def to_sym
15
+ case @type
16
+ when "D" then :datetime
17
+ when "AN" then :alphanumeric
18
+ when "N" then :numeric
19
+ end
20
+ end
21
+
22
+ def inspect
23
+ "<FieldValidator #{to_sym}[#{@size}]>"
24
+ end
25
+
26
+ def validate(value)
27
+ return false if @required && value.nil?
28
+ return true if !@required && value.nil?
29
+ value = value.to_s
30
+ case @type
31
+ # Examples throughout §5 suggest this date format.
32
+ # See §5.1.3. Example for instance.
33
+ when "D"
34
+ size = @size.zero? ? 14 : @size
35
+ return !!(value =~ /^\d{#{size}}$/) && (!!Time.parse(value) rescue false)
36
+ # This is a liberal interpretation of alphanumeric, but examples like
37
+ # §5.3.1 (IPADDRESS), §5.28.1 (CITY) suggest that spaces and punctuation
38
+ # are acceptable as well. This leaves us just checking max size.
39
+ when "AN"
40
+ return (value.size <= @size)
41
+ # §5.28.1 (AMOUNT) seems to allow for size <= the specifier.
42
+ when "N"
43
+ return !!(value =~ /^\d{1,#{@size}}$/)
44
+ end
45
+ end
46
+ end
47
+ end