global_collect 0.1.0

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