activemerchant 1.11.0 → 1.12.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 (27) hide show
  1. data.tar.gz.sig +3 -1
  2. data/CHANGELOG +17 -0
  3. data/CONTRIBUTORS +16 -0
  4. data/README.rdoc +5 -0
  5. data/lib/active_merchant.rb +0 -1
  6. data/lib/active_merchant/billing/gateway.rb +1 -1
  7. data/lib/active_merchant/billing/gateways/authorize_net.rb +5 -2
  8. data/lib/active_merchant/billing/gateways/federated_canada.rb +169 -0
  9. data/lib/active_merchant/billing/gateways/garanti.rb +13 -1
  10. data/lib/active_merchant/billing/gateways/merchant_e_solutions.rb +4 -4
  11. data/lib/active_merchant/billing/gateways/nmi.rb +13 -0
  12. data/lib/active_merchant/billing/gateways/pay_junction.rb +8 -8
  13. data/lib/active_merchant/billing/gateways/paybox_direct.rb +3 -4
  14. data/lib/active_merchant/billing/gateways/paypal/paypal_common_api.rb +2 -2
  15. data/lib/active_merchant/billing/gateways/paypal_express.rb +37 -5
  16. data/lib/active_merchant/billing/gateways/qbms.rb +290 -0
  17. data/lib/active_merchant/billing/integrations/direc_pay/helper.rb +25 -21
  18. data/lib/active_merchant/billing/integrations/moneybookers.rb +23 -0
  19. data/lib/active_merchant/billing/integrations/moneybookers/helper.rb +33 -0
  20. data/lib/active_merchant/billing/integrations/moneybookers/notification.rb +101 -0
  21. data/lib/active_merchant/billing/integrations/valitor/helper.rb +5 -1
  22. data/lib/active_merchant/billing/integrations/world_pay.rb +34 -0
  23. data/lib/active_merchant/billing/integrations/world_pay/helper.rb +100 -0
  24. data/lib/active_merchant/billing/integrations/world_pay/notification.rb +160 -0
  25. data/lib/active_merchant/version.rb +1 -1
  26. metadata +13 -4
  27. metadata.gz.sig +0 -0
@@ -0,0 +1,23 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ module Integrations #:nodoc:
4
+ module Moneybookers
5
+
6
+ autoload :Notification, File.dirname(__FILE__) + '/moneybookers/notification.rb'
7
+ autoload :Helper, File.dirname(__FILE__) + '/moneybookers/helper.rb'
8
+
9
+ mattr_accessor :production_url
10
+ self.production_url = 'https://www.moneybookers.com/app/payment.pl'
11
+
12
+ def self.service_url
13
+ self.production_url
14
+ end
15
+
16
+ def self.notification(post)
17
+ Notification.new(post)
18
+ end
19
+
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,33 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ module Integrations #:nodoc:
4
+ module Moneybookers
5
+ class Helper < ActiveMerchant::Billing::Integrations::Helper
6
+ mapping :account, 'pay_to_email'
7
+ mapping :order, 'transaction_id'
8
+ mapping :amount, 'amount'
9
+ mapping :currency, 'currency'
10
+
11
+ mapping :customer,
12
+ :first_name => 'firstname',
13
+ :last_name => 'lastname',
14
+ :email => 'pay_from_email',
15
+ :phone => 'phone_number'
16
+
17
+ mapping :billing_address,
18
+ :city => 'city',
19
+ :address1 => 'address',
20
+ :address2 => 'address2',
21
+ :state => 'state',
22
+ :zip => 'postal_code',
23
+ :country => 'country'
24
+
25
+ mapping :notify_url, 'status_url'
26
+ mapping :return_url, 'return_url'
27
+ mapping :cancel_return_url, 'cancel_url'
28
+ mapping :description, 'detail1_text'
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,101 @@
1
+ require 'net/http'
2
+ require 'digest/md5'
3
+
4
+ module ActiveMerchant #:nodoc:
5
+ module Billing #:nodoc:
6
+ module Integrations #:nodoc:
7
+ module Moneybookers
8
+ class Notification < ActiveMerchant::Billing::Integrations::Notification
9
+ include PostsData
10
+
11
+ # was the transaction comlete?
12
+ def complete?
13
+ status == "2"
14
+ end
15
+
16
+ def status
17
+ params['status']
18
+ end
19
+
20
+ def item_id
21
+ nil
22
+ end
23
+
24
+ def transaction_id
25
+ if params.has_key?("transaction_id")
26
+ params['transaction_id']
27
+ else
28
+ params['mb_transaction_id']
29
+ end
30
+ end
31
+
32
+ # When was this payment received by the client.
33
+ def received_at
34
+ nil
35
+ end
36
+
37
+ def payer_email
38
+ params['pay_from_email']
39
+ end
40
+
41
+ def receiver_email
42
+ params['pay_to_email']
43
+ end
44
+
45
+ def md5sig
46
+ params['md5sig']
47
+ end
48
+
49
+ #Unique ID from the merchant's Moneybookers.com account, needed for calculatinon of md5 sig
50
+ def merchant_id
51
+ params['merchant_id']
52
+ end
53
+
54
+ # currency of mb_amount, will always be the same as the currency of the beneficiary's account at Moneybookers.com
55
+ def currency
56
+ params['mb_currency']
57
+ end
58
+
59
+ # total amount of the payment in Merchants currency (ex 25.46/25.4/25)
60
+ def gross
61
+ params['mb_amount']
62
+ end
63
+
64
+ # currency of the payment as posted by the merchant on the entry form
65
+ def posted_currency
66
+ params['currency']
67
+ end
68
+
69
+ # amount of the payment as posted by the merchant on the entry form (ex. 39.60/39.6/39)
70
+ def posted_amount
71
+ params['amount']
72
+ end
73
+
74
+ # Was this a test transaction?
75
+ def test?
76
+ false
77
+ end
78
+
79
+ # Acknowledge the transaction to MoneyBooker. This method has to be called after a new
80
+ # apc arrives. It will verify that all the information we received is correct and will return a
81
+ # ok or a fail.
82
+ #
83
+ # Example:
84
+ #
85
+ # def ipn
86
+ # notify = Moneybookers::Notification.new(request.raw_post)
87
+ #
88
+ # if notify.acknowledge('secretpass')
89
+ # ... process order ... if notify.complete?
90
+ # else
91
+ # ... log possible hacking attempt ...
92
+ # end
93
+ def acknowledge(secret = '')
94
+ fields = [merchant_id, transaction_id, Digest::MD5.hexdigest(secret).upcase, gross, currency, status].join
95
+ md5sig == Digest::MD5.hexdigest(fields).upcase
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -50,7 +50,7 @@ module ActiveMerchant #:nodoc:
50
50
  requires!(options, :amount, :description)
51
51
  options.assert_valid_keys([:description, :quantity, :amount, :discount])
52
52
 
53
- add_field("Vara_#{id}_Verd", options[:amount])
53
+ add_field("Vara_#{id}_Verd", format_amount(options[:amount]))
54
54
  add_field("Vara_#{id}_Fjoldi", options[:quantity] || "1")
55
55
 
56
56
  add_field("Vara_#{id}_Lysing", options[:description]) if options[:description]
@@ -75,6 +75,10 @@ module ActiveMerchant #:nodoc:
75
75
  @fields[mappings[:success_text]] ||= DEFAULT_SUCCESS_TEXT
76
76
  @fields.merge('RafraenUndirskrift' => signature)
77
77
  end
78
+
79
+ def format_amount(amount)
80
+ amount.to_f.round
81
+ end
78
82
  end
79
83
  end
80
84
  end
@@ -0,0 +1,34 @@
1
+ require File.dirname(__FILE__) + '/world_pay/helper.rb'
2
+ require File.dirname(__FILE__) + '/world_pay/notification.rb'
3
+
4
+ module ActiveMerchant #:nodoc:
5
+ module Billing #:nodoc:
6
+ module Integrations #:nodoc:
7
+ module WorldPay
8
+
9
+ mattr_accessor :service_url
10
+ self.service_url = 'https://select.worldpay.com/wcc/purchase'
11
+
12
+ mattr_accessor :test_url
13
+ self.test_url = 'https://select-test.worldpay.com/wcc/purchase'
14
+
15
+
16
+ def self.service_url
17
+ mode = ActiveMerchant::Billing::Base.integration_mode
18
+ case mode
19
+ when :production
20
+ production_url
21
+ when :test
22
+ test_url
23
+ else
24
+ raise StandardError, "Integration mode set to an invalid value: #{mode}"
25
+ end
26
+ end
27
+
28
+ def self.notification(post)
29
+ Notification.new(post)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,100 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ module Integrations #:nodoc:
4
+ module WorldPay
5
+ class Helper < ActiveMerchant::Billing::Integrations::Helper
6
+ mapping :account, 'instId'
7
+ mapping :amount, 'amount'
8
+ mapping :order, 'cartId'
9
+ mapping :currency, 'currency'
10
+
11
+ mapping :customer, :email => 'email',
12
+ :phone => 'tel'
13
+
14
+ mapping :billing_address, :zip => 'postcode',
15
+ :country => 'country'
16
+
17
+ mapping :description, 'desc'
18
+ mapping :notify_url, 'MC_callback'
19
+
20
+
21
+ # WorldPay supports two different test modes - :always_succeed and :always_fail
22
+ def initialize(order, account, options = {})
23
+ super
24
+
25
+ if ActiveMerchant::Billing::Base.integration_mode == :test || options[:test]
26
+ test_mode = case options[:test]
27
+ when :always_fail
28
+ 101
29
+ when false
30
+ 0
31
+ else
32
+ 100
33
+ end
34
+ add_field('testMode', test_mode.to_s)
35
+ elsif ActiveMerchant::Billing::Base.integration_mode == :always_succeed
36
+ add_field('testMode', '100')
37
+ elsif ActiveMerchant::Billing::Base.integration_mode == :always_fail
38
+ add_field('testMode', '101')
39
+ end
40
+ end
41
+
42
+ # WorldPay only supports a single address field so we
43
+ # have to concat together - lines are separated using &#10;
44
+ def billing_address(params={})
45
+ add_field(mappings[:billing_address][:zip], params[:zip])
46
+ add_field(mappings[:billing_address][:country], lookup_country_code(params[:country]))
47
+
48
+ address = [params[:address1], params[:address2], params[:city], params[:state]].compact
49
+ add_field('address', address.join('&#10;'))
50
+ end
51
+
52
+ # WorldPay only supports a single name field so we have to concat
53
+ def customer(params={})
54
+ add_field(mappings[:customer][:email], params[:email])
55
+ add_field(mappings[:customer][:phone], params[:phone])
56
+ add_field('name', "#{params[:first_name]} #{params[:last_name]}")
57
+ end
58
+
59
+ # Support for a MD5 hash of selected fields to prevent tampering
60
+ # For futher information read the tech note at the address below:
61
+ # http://support.worldpay.com/kb/integration_guides/junior/integration/help/tech_notes/sjig_tn_009.html
62
+ def encrypt(secret, fields = [:amount, :currency, :account, :order])
63
+ signature_fields = fields.collect{ |field| mappings[field] }
64
+ add_field('signatureFields', signature_fields.join(':'))
65
+
66
+ field_values = fields.collect{ |field| form_fields[mappings[field]] }
67
+ signature = "#{secret}:#{field_values.join(':')}"
68
+ add_field('signature', Digest::MD5.hexdigest(signature))
69
+ end
70
+
71
+ # Add a time window for which the payment can be completed. Read the link below for how they work
72
+ # http://support.worldpay.com/kb/integration_guides/junior/integration/help/appendicies/sjig_10100.html
73
+ def valid_from(from_time)
74
+ add_field('authValidFrom', from_time.to_i.to_s + '000')
75
+ end
76
+
77
+ def valid_to(to_time)
78
+ add_field('authValidTo', to_time.to_i.to_s + '000')
79
+ end
80
+
81
+ # WorldPay supports the passing of custom parameters prefixed with the following:
82
+ # C_ : These parameters can be used in the response pages hosted on WorldPay's site
83
+ # M_ : These parameters are passed through to the callback script (if enabled)
84
+ # MC_ or CM_ : These parameters are availble both in the response and callback contexts
85
+ def response_params(params={})
86
+ params.each{|k,v| add_field("C_#{k}",v)}
87
+ end
88
+
89
+ def callback_params(params={})
90
+ params.each{|k,v| add_field("M_#{k}",v)}
91
+ end
92
+
93
+ def combined_params(params={})
94
+ params.each{|k,v| add_field("MC_#{k}",v)}
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,160 @@
1
+ module ActiveMerchant #:nodoc:
2
+ module Billing #:nodoc:
3
+ module Integrations #:nodoc:
4
+ module WorldPay
5
+ class Notification < ActiveMerchant::Billing::Integrations::Notification
6
+ def complete?
7
+ status == 'Completed'
8
+ end
9
+
10
+ def account
11
+ params['instId']
12
+ end
13
+
14
+ def item_id
15
+ params['cartId']
16
+ end
17
+
18
+ def transaction_id
19
+ params['transId']
20
+ end
21
+
22
+ # Time this payment was received by the client in UTC time.
23
+ def received_at
24
+ Time.at(params['transTime'].to_i / 1000).utc
25
+ end
26
+
27
+ # Callback password set in the WorldPay CMS
28
+ def security_key
29
+ params['callbackPW']
30
+ end
31
+
32
+ # the money amount we received in X.2 decimal.
33
+ def gross
34
+ params['authAmount']
35
+ end
36
+
37
+ def currency
38
+ params['authCurrency']
39
+ end
40
+
41
+ # Was this a test transaction?
42
+ def test?
43
+ params.key?('testMode') && params['testMode'] != '0'
44
+ end
45
+
46
+ def status
47
+ params['transStatus'] == 'Y' ? 'Completed' : 'Cancelled'
48
+ end
49
+
50
+ def name
51
+ params['name']
52
+ end
53
+
54
+ def address
55
+ params['address']
56
+ end
57
+
58
+ def postcode
59
+ params['postcode']
60
+ end
61
+
62
+ def country
63
+ params['country']
64
+ end
65
+
66
+ def phone_number
67
+ params['tel']
68
+ end
69
+
70
+ def fax_number
71
+ params['fax']
72
+ end
73
+
74
+ def email_address
75
+ params['email']
76
+ end
77
+
78
+ def card_type
79
+ params['cardType']
80
+ end
81
+
82
+ # WorldPay extended fraud checks returned as a 4 character string
83
+ # 1st char: Credit card CVV check
84
+ # 2nd char: Postcode AVS check
85
+ # 3rd char: Address AVS check
86
+ # 4th char: Country comparison check
87
+ # Possible values are:
88
+ # :not_supported - 0
89
+ # :not_checked - 1
90
+ # :matched - 2
91
+ # :not_matched - 4
92
+ # :partial_match - 8
93
+ def cvv_status
94
+ return avs_value_to_symbol(params['AVS'][0].chr)
95
+ end
96
+
97
+ def postcode_status
98
+ return avs_value_to_symbol(params['AVS'][1].chr)
99
+ end
100
+
101
+ def address_status
102
+ return avs_value_to_symbol(params['AVS'][2].chr)
103
+ end
104
+
105
+ def country_status
106
+ return avs_value_to_symbol(params['AVS'][3].chr)
107
+ end
108
+
109
+ def acknowledge
110
+ return true
111
+ end
112
+
113
+ # WorldPay supports the passing of custom parameters through to the callback script
114
+ def custom_params
115
+ return @custom_params ||= read_custom_params
116
+ end
117
+
118
+
119
+ private
120
+
121
+ # Take the posted data and move the relevant data into a hash
122
+ def parse(post)
123
+ @raw = post
124
+ for line in post.split('&')
125
+ key, value = *line.scan( %r{^(\w+)\=(.*)$} ).flatten
126
+ params[key] = value
127
+ end
128
+ end
129
+
130
+ # Read the custom params into a hash
131
+ def read_custom_params
132
+ custom = {}
133
+ params.each do |key, value|
134
+ if /\A(M_|MC_|CM_)/ === key
135
+ custom[key.gsub(/\A(M_|MC_|CM_)/, '').to_sym] = value
136
+ end
137
+ end
138
+ custom
139
+ end
140
+
141
+ # Convert a AVS value to a symbol - see above for more about AVS
142
+ def avs_value_to_symbol(value)
143
+ case value.to_s
144
+ when '8'
145
+ :partial_match
146
+ when '4'
147
+ :no_match
148
+ when '2'
149
+ :matched
150
+ when '1'
151
+ :not_checked
152
+ else
153
+ :not_supported
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end