offsite_payments 2.1.0 → 2.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 657879b046bcb541ea2225eee85af0f5916a4d79
4
- data.tar.gz: 73e5e75b10f09b131966a268697dd641dfc1bd93
3
+ metadata.gz: b93c09319f5c58335021b513de7d370d04369846
4
+ data.tar.gz: 27db91e8bc8007d62fd62c10bbedbc301b5e87e8
5
5
  SHA512:
6
- metadata.gz: 697047cf3a0639a5093c1f1ba0c5a19c9040a4694405ea7ca515b0a2bcabb3a52527ac97e49919b8fcd42fd1c27b7f10ead8d55c9aea4597eb4cdcbc20308bc3
7
- data.tar.gz: 22c3e6d5e428a00fd8943d15ad95ae3e811716d8cbbdf778cb7f881f16665b952a63223a786274b616c6741118ab1f00feef0dd87dbabb04d012ab966b4a3440
6
+ metadata.gz: 2e3892c9fc35ef7949b8b98ea8521bdf33d98c58b2e8a8ff220d3d7fcabd5efc4de1d0bca75ca9431d1df4374d14d938aa58e7fba60cd0a4693d46180ab5db53
7
+ data.tar.gz: cc5bb2b3a068fb481f6a09365ad359005fd31d5a2d202d93e143b00287653b7a5ea76533b41d90c0cbe43b375bf33eb4aa3a9b30bd89bb448b0cf4bb0fdf7223
data/README.md CHANGED
@@ -1,13 +1,23 @@
1
1
  # Offsite Payments
2
- [![Build Status](https://travis-ci.org/Shopify/offsite_payments.png?branch=master)](https://travis-ci.org/Shopify/offsite_payments)
3
- [![Code Climate](https://codeclimate.com/github/Shopify/offsite_payments.png)](https://codeclimate.com/github/Shopify/offsite_payments)
2
+ [![Build Status](https://travis-ci.org/activemerchant/offsite_payments.png?branch=master)](https://travis-ci.org/activemerchant/offsite_payments)
3
+ [![Code Climate](https://codeclimate.com/github/activemerchant/offsite_payments.png)](https://codeclimate.com/github/activemerchant/offsite_payments)
4
4
 
5
5
  Offsite Payments is an extraction from the ecommerce system [Shopify](http://www.shopify.com). Shopify's requirements for a simple and unified API to handle dozens of different offsite payment pages (often called hosted payment pages) with very different exposed APIs was the chief principle in designing the library.
6
6
 
7
7
  It was developed for usage in Ruby on Rails web applications and integrates seamlessly
8
8
  as a Rails plugin. It should also work as a stand alone Ruby library, but much of the benefit is in the ActionView helpers which are Rails-specific.
9
9
 
10
- Offsite Payments has been in production use (originally as part of the [ActiveMerchant](https://github.com/Shopify/active_merchant) project) since June 2006. It is maintained by the [Shopify](http://www.shopify.com) team, with much help from an ever-growing set of contributors.
10
+ Offsite Payments has been in production use (originally as part of the [ActiveMerchant](https://github.com/activemerchant/active_merchant) project) since June 2006. It is maintained by the [Shopify](http://www.shopify.com) team, with much help from an ever-growing set of contributors.
11
+
12
+ The addition of your gateway to offsite_payments does not guarantee placement within Shopify. In order to have your gateway considered, please send an email to payment-integrations@shopify.com with **Offsite Payments Integration** in the subject. Be sure to include:
13
+
14
+ 1. Name, URL & description of the payment provider you wish to integrate
15
+ 2. Markets served by this integration
16
+ 3. List of major supported payment methods
17
+ 4. Your most recent Certificate of PCI Compliance
18
+ 5. Reason that the [Universal API](https://github.com/activemerchant/offsite_payments/blob/master/lib/offsite_payments/integrations/universal.rb)* cannot be used for your integration.
19
+
20
+ *The Universal API defines a standard set of requests and callbacks that can be used to integrate with Shopify. A sample app and documentation are hosted [here](https://github.com/Shopify/offsite-gateway-sim). The Universal API should be used for all integrations in which placement within Shopify is the desired outcome.
11
21
 
12
22
  ## Installation
13
23
 
@@ -15,7 +25,7 @@ Offsite Payments has been in production use (originally as part of the [ActiveMe
15
25
 
16
26
  You can check out the latest source from git:
17
27
 
18
- git clone https://github.com/Shopify/offsite_payments.git
28
+ git clone https://github.com/activemerchant/offsite_payments.git
19
29
 
20
30
  ### From RubyGems
21
31
 
@@ -27,7 +37,7 @@ Or, if you're using Bundler, just add the following to your Gemfile:
27
37
 
28
38
  gem 'offsite_payments'
29
39
 
30
- [API documentation](http://rubydoc.info/github/Shopify/offsite_payments/master/file/README.md).
40
+ [API documentation](http://www.rubydoc.info/github/activemerchant/offsite_payments/master).
31
41
 
32
42
  ## Supported Integrations
33
43
 
@@ -45,6 +55,7 @@ Or, if you're using Bundler, just add the following to your Gemfile:
45
55
  * [ePay](http://www.epay.dk/epay-payment-solutions/)
46
56
  * [First Data](https://firstdata.zendesk.com/entries/407522-first-data-global-gateway-e4sm-payment-pages-integration-manual)
47
57
  * [HiTRUST](http://www.hitrust.com.hk/)
58
+ * [MOLPay](http://www.molpay.com/v2/) - MY, SG, ID, TH, VN, PH, CN, AU
48
59
  * [Moneybookers](http://www.moneybookers.com)
49
60
  * [Nochex](http://www.nochex.com)
50
61
  * [PagSeguro](http://www.pagseguro.com.br/) - BR
@@ -53,6 +64,7 @@ Or, if you're using Bundler, just add the following to your Gemfile:
53
64
  * [PayDollar](http://www.paydollar.com)
54
65
  * [Paysbuy](https://www.paysbuy.com/) - TH
55
66
  * [Platron](https://www.platron.ru/) - RU
67
+ * [Realex](http://www.realexpayments.com)
56
68
  * [RBK Money](https://rbkmoney.ru/) - RU
57
69
  * [Robokassa](http://robokassa.ru/) - RU
58
70
  * [SagePay Form](http://www.sagepay.com/products_services/sage_pay_go/integration/form)
@@ -3,8 +3,6 @@ require 'cgi'
3
3
  require "timeout"
4
4
  require "socket"
5
5
 
6
- require 'active_support/core_ext/class/delegating_attributes'
7
-
8
6
  require 'active_utils'
9
7
 
10
8
  require "offsite_payments/helper"
@@ -16,7 +16,7 @@ module OffsitePayments #:nodoc:
16
16
  end
17
17
 
18
18
  def initialize(order, account, options = {})
19
- options.assert_valid_keys([:amount, :currency, :test, :credential2, :credential3, :credential4, :country, :account_name, :description, :transaction_type, :authcode, :notify_url, :return_url, :redirect_param, :forward_url])
19
+ options.assert_valid_keys([:amount, :currency, :test, :credential2, :credential3, :credential4, :country, :account_name, :description, :transaction_type, :authcode, :notify_url, :return_url, :redirect_param, :forward_url, :checkout_token])
20
20
  @fields = {}
21
21
  @raw_html_fields = []
22
22
  @test = options[:test]
@@ -30,6 +30,7 @@ module OffsitePayments #:nodoc:
30
30
  self.notify_url = options[:notify_url]
31
31
  self.return_url = options[:return_url]
32
32
  self.redirect_param = options[:redirect_param]
33
+ self.checkout_token = options[:checkout_token]
33
34
  end
34
35
 
35
36
  def self.mapping(attribute, options = {})
@@ -84,7 +84,7 @@ module OffsitePayments
84
84
  else
85
85
  case transaction_status.downcase
86
86
  when 'success' then 'Completed'
87
- when 'canceled' then 'Cancelled'
87
+ when 'canceled' then 'Failed'
88
88
  end
89
89
  end
90
90
  else
@@ -219,7 +219,7 @@ module OffsitePayments
219
219
  end
220
220
 
221
221
  def cancelled?
222
- @notification.status == 'Cancelled'
222
+ @notification.status == 'Failed'
223
223
  end
224
224
  end
225
225
  end
@@ -81,7 +81,11 @@ module OffsitePayments #:nodoc:
81
81
  end
82
82
 
83
83
  def gross
84
- "%.2f" % (params['total_native']['cents'].to_f / 100)
84
+ if params['total_original'].present?
85
+ "%.2f" % (params['total_original']['cents'].to_f / 100)
86
+ else
87
+ "%.2f" % (params['total_native']['cents'].to_f / 100)
88
+ end
85
89
  end
86
90
 
87
91
  def currency
@@ -101,7 +105,6 @@ module OffsitePayments #:nodoc:
101
105
  # apc arrives. Coinbase will verify that all the information we received are correct
102
106
  # and will return a ok or a fail.
103
107
  def acknowledge(authcode = {})
104
-
105
108
  uri = URI.parse(Coinbase.notification_confirmation_url % transaction_id)
106
109
 
107
110
  response = Coinbase.do_request(uri, @options[:credential1], @options[:credential2])
@@ -110,6 +113,7 @@ module OffsitePayments #:nodoc:
110
113
  posted_order = @params
111
114
  parse(response)
112
115
 
116
+ return false unless @params
113
117
  %w(id custom total_native status).all? { |param| posted_order[param] == @params[param] }
114
118
  end
115
119
 
@@ -118,6 +122,8 @@ module OffsitePayments #:nodoc:
118
122
  def parse(post)
119
123
  @raw = post.to_s
120
124
  @params = JSON.parse(post)['order']
125
+ rescue JSON::ParserError
126
+ @params = {}
121
127
  end
122
128
  end
123
129
 
@@ -289,7 +289,7 @@ module OffsitePayments #:nodoc:
289
289
 
290
290
  class Return < OffsitePayments::Return
291
291
  def initialize(post_data, options = {})
292
- @notification = Notification.new(treat_failure_as_pending(post_data), options)
292
+ @notification = Notification.new(post_data, options)
293
293
  end
294
294
 
295
295
  def success?
@@ -299,13 +299,6 @@ module OffsitePayments #:nodoc:
299
299
  def message
300
300
  notification.status
301
301
  end
302
-
303
- private
304
-
305
- # Work around the issue that the initial return from DirecPay is always either SUCCESS or FAIL, there is no PENDING
306
- def treat_failure_as_pending(post_data)
307
- post_data.sub(/FAIL/, 'PENDING')
308
- end
309
302
  end
310
303
 
311
304
  class Status
@@ -189,6 +189,8 @@ module OffsitePayments #:nodoc:
189
189
  response = ssl_get(Gestpay.service_url, decryption_query_string(shop_login, encrypted_string))
190
190
  encoded_response = parse_response(response)
191
191
  parse_delimited_string(encoded_response, DELIMITER, true)
192
+ rescue GestpayEncryptionResponseError => e
193
+ { 'PAY1_TRANSACTIONRESULT' => 'Error' }
192
194
  end
193
195
 
194
196
  def decryption_query_string(shop_login, encrypted_string)
@@ -2,7 +2,9 @@ module OffsitePayments #:nodoc:
2
2
  module Integrations #:nodoc:
3
3
  module Klarna
4
4
  mattr_accessor :service_url
5
- self.service_url = 'https://api.hostedcheckout.io/api/v1/checkout'
5
+ self.service_url = 'https://api.hostedcheckout.io/shopify/payment'
6
+
7
+ REQUIRED_FIELDS = %w(amount checkout_token merchant_base_uri merchant_checkout_uri merchant_confirmation_uri merchant_id merchant_terms_uri purchase_currency)
6
8
 
7
9
  def self.notification(post_body, options = {})
8
10
  Notification.new(post_body, options)
@@ -15,24 +17,11 @@ module OffsitePayments #:nodoc:
15
17
  def self.cart_items_payload(fields, cart_items)
16
18
  check_required_fields!(fields)
17
19
 
18
- payload = fields['purchase_country'].to_s +
19
- fields['purchase_currency'].to_s +
20
- fields['locale'].to_s
21
-
22
- cart_items.each_with_index do |item, i|
23
- payload << fields["cart_item-#{i}_type"].to_s +
24
- fields["cart_item-#{i}_reference"].to_s +
25
- fields["cart_item-#{i}_quantity"].to_s +
26
- fields["cart_item-#{i}_unit_price"].to_s +
27
- fields.fetch("cart_item-#{i}_discount_rate", '').to_s
20
+ payload = ""
21
+ REQUIRED_FIELDS.sort.each do |field|
22
+ payload << fields[field].to_s
28
23
  end
29
24
 
30
- payload << fields['merchant_id'].to_s +
31
- fields['merchant_terms_uri'].to_s +
32
- fields['merchant_checkout_uri'].to_s +
33
- fields['merchant_base_uri'].to_s +
34
- fields['merchant_confirmation_uri'].to_s
35
-
36
25
  payload
37
26
  end
38
27
 
@@ -49,14 +38,7 @@ module OffsitePayments #:nodoc:
49
38
  private
50
39
 
51
40
  def self.check_required_fields!(fields)
52
- %w(purchase_country
53
- purchase_currency
54
- locale
55
- merchant_id
56
- merchant_terms_uri
57
- merchant_checkout_uri
58
- merchant_base_uri
59
- merchant_confirmation_uri).each do |required_field|
41
+ REQUIRED_FIELDS.each do |required_field|
60
42
  raise ArgumentError, "Missing required field #{required_field}" if fields[required_field].nil?
61
43
  end
62
44
  end
@@ -66,6 +48,8 @@ module OffsitePayments #:nodoc:
66
48
  mapping :cancel_return_url, ['merchant_terms_uri', 'merchant_checkout_uri', 'merchant_base_uri']
67
49
  mapping :account, 'merchant_id'
68
50
  mapping :customer, email: 'shipping_address_email'
51
+ mapping :checkout_token, 'checkout_token'
52
+ mapping :amount, 'amount'
69
53
 
70
54
  def initialize(order, account, options = {})
71
55
  super
@@ -234,7 +218,7 @@ module OffsitePayments #:nodoc:
234
218
  end
235
219
 
236
220
  def gross_cents
237
- params["cart"]["total_price_including_tax"]
221
+ params["order_amount"]
238
222
  end
239
223
 
240
224
  def status
@@ -0,0 +1,32 @@
1
+ module OffsitePayments #:nodoc:
2
+ module Integrations #:nodoc:
3
+ module Mollie
4
+ class API
5
+ include ActiveUtils::PostsData
6
+
7
+ attr_reader :token
8
+
9
+ MOLLIE_API_V1_URI = 'https://api.mollie.nl/v1/'.freeze
10
+
11
+ def initialize(token)
12
+ @token = token
13
+ end
14
+
15
+ def get_request(resource, params = nil)
16
+ uri = URI.parse(MOLLIE_API_V1_URI + resource)
17
+ uri.query = params.map { |k,v| "#{CGI.escape(k)}=#{CGI.escape(v)}}"}.join('&') if params
18
+ headers = { "Authorization" => "Bearer #{token}", "Content-Type" => "application/json" }
19
+ JSON.parse(ssl_get(uri.to_s, headers))
20
+ end
21
+
22
+ def post_request(resource, params = nil)
23
+ uri = URI.parse(MOLLIE_API_V1_URI + resource)
24
+ headers = { "Authorization" => "Bearer #{token}", "Content-Type" => "application/json" }
25
+ data = params.nil? ? nil : JSON.dump(params)
26
+ JSON.parse(ssl_post(uri.to_s, data, headers))
27
+ end
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -1,33 +1,7 @@
1
1
  module OffsitePayments #:nodoc:
2
2
  module Integrations #:nodoc:
3
3
  module MollieIdeal
4
- class API
5
- include ActiveUtils::PostsData
6
-
7
- attr_reader :token
8
-
9
- def initialize(token)
10
- @token = token
11
- end
12
-
13
- def get_request(resource, params = nil)
14
- uri = URI.parse(MOLLIE_API_V1_URI + resource)
15
- uri.query = params.map { |k,v| "#{CGI.escape(k)}=#{CGI.escape(v)}}"}.join('&') if params
16
- headers = { "Authorization" => "Bearer #{token}", "Content-Type" => "application/json" }
17
- JSON.parse(ssl_get(uri.to_s, headers))
18
- end
19
-
20
- def post_request(resource, params = nil)
21
- uri = URI.parse(MOLLIE_API_V1_URI + resource)
22
- headers = { "Authorization" => "Bearer #{token}", "Content-Type" => "application/json" }
23
- data = params.nil? ? nil : JSON.dump(params)
24
- JSON.parse(ssl_post(uri.to_s, data, headers))
25
- end
26
- end
27
-
28
- RedirectError = Class.new(ActiveUtils::ActiveUtilsError)
29
-
30
- MOLLIE_API_V1_URI = 'https://api.mollie.nl/v1/'.freeze
4
+ include Mollie
31
5
 
32
6
  mattr_accessor :live_issuers
33
7
  self.live_issuers = [
@@ -48,22 +22,6 @@ module OffsitePayments #:nodoc:
48
22
  ["TBM Bank", "ideal_TESTNL99"]
49
23
  ]
50
24
 
51
- def self.notification(post, options = {})
52
- Notification.new(post, options)
53
- end
54
-
55
- def self.return(post, options = {})
56
- Return.new(post, options)
57
- end
58
-
59
- def self.live?
60
- OffsitePayments.mode == :production
61
- end
62
-
63
- def self.requires_redirect_param?
64
- true
65
- end
66
-
67
25
  def self.redirect_param_label
68
26
  "Select your bank"
69
27
  end
@@ -80,6 +38,22 @@ module OffsitePayments #:nodoc:
80
38
  .map { |issuer| [issuer['name'], issuer['id']] }
81
39
  end
82
40
 
41
+ RedirectError = Class.new(ActiveUtils::ActiveUtilsError)
42
+
43
+ MOLLIE_API_V1_URI = 'https://api.mollie.nl/v1/'.freeze
44
+
45
+ def self.notification(post, options = {})
46
+ Notification.new(post, options)
47
+ end
48
+
49
+ def self.return(post, options = {})
50
+ Return.new(post, options)
51
+ end
52
+
53
+ def self.live?
54
+ OffsitePayments.mode == :production
55
+ end
56
+
83
57
  def self.create_payment(token, params)
84
58
  API.new(token).post_request('payments', params)
85
59
  end
@@ -88,12 +62,16 @@ module OffsitePayments #:nodoc:
88
62
  API.new(token).get_request("payments/#{payment_id}")
89
63
  end
90
64
 
65
+ def self.requires_redirect_param?
66
+ true
67
+ end
68
+
91
69
  class Helper < OffsitePayments::Helper
92
- attr_reader :transaction_id, :redirect_paramaters, :token
70
+ attr_reader :transaction_id, :redirect_parameters, :token
93
71
 
94
72
  def initialize(order, account, options = {})
95
73
  @token = account
96
- @redirect_paramaters = {
74
+ @redirect_parameters = {
97
75
  :amount => options[:amount],
98
76
  :description => options[:description],
99
77
  :issuer => options[:redirect_param],
@@ -102,7 +80,7 @@ module OffsitePayments #:nodoc:
102
80
  :metadata => { :order => order }
103
81
  }
104
82
 
105
- @redirect_paramaters[:webhookUrl] = options[:notify_url] if options[:notify_url]
83
+ @redirect_parameters[:webhookUrl] = options[:notify_url] if options[:notify_url]
106
84
 
107
85
  super
108
86
 
@@ -136,7 +114,7 @@ module OffsitePayments #:nodoc:
136
114
  end
137
115
 
138
116
  def request_redirect
139
- MollieIdeal.create_payment(token, redirect_paramaters)
117
+ MollieIdeal.create_payment(token, redirect_parameters)
140
118
  rescue ActiveUtils::ResponseError => e
141
119
  case e.response.code
142
120
  when '401', '403', '422'
@@ -0,0 +1,143 @@
1
+ module OffsitePayments #:nodoc:
2
+ module Integrations #:nodoc:
3
+ module MollieMistercash
4
+ include Mollie
5
+
6
+ RedirectError = Class.new(ActiveUtils::ActiveUtilsError)
7
+
8
+ def self.notification(post, options = {})
9
+ Notification.new(post, options)
10
+ end
11
+
12
+ def self.return(post, options = {})
13
+ Return.new(post, options)
14
+ end
15
+
16
+ def self.live?
17
+ OffsitePayments.mode == :production
18
+ end
19
+
20
+ def self.create_payment(token, params)
21
+ API.new(token).post_request('payments', params)
22
+ end
23
+
24
+ def self.check_payment_status(token, payment_id)
25
+ API.new(token).get_request("payments/#{payment_id}")
26
+ end
27
+
28
+ def self.requires_redirect_param?
29
+ false
30
+ end
31
+
32
+ class Helper < OffsitePayments::Helper
33
+ attr_reader :transaction_id, :redirect_parameters, :token
34
+
35
+ def initialize(order, account, options = {})
36
+ @token = account
37
+ @redirect_parameters = {
38
+ :amount => options[:amount],
39
+ :description => options[:description],
40
+ :redirectUrl => options[:return_url],
41
+ :method => 'mistercash',
42
+ :metadata => { :order => order }
43
+ }
44
+
45
+ @redirect_parameters[:webhookUrl] = options[:notify_url] if options[:notify_url]
46
+
47
+ super
48
+
49
+ raise ArgumentError, "The return_url option needs to be set." if options[:return_url].blank?
50
+ raise ArgumentError, "The description option needs to be set." if options[:description].blank?
51
+ end
52
+
53
+ def credential_based_url
54
+ response = request_redirect
55
+ uri = URI.parse(response['links']['paymentUrl'])
56
+ uri.to_s
57
+ end
58
+
59
+ def form_method
60
+ "GET"
61
+ end
62
+
63
+ def request_redirect
64
+ MollieMistercash.create_payment(token, redirect_parameters)
65
+ rescue ActiveUtils::ResponseError => e
66
+ case e.response.code
67
+ when '401', '403', '422'
68
+ error = JSON.parse(e.response.body)['error']['message']
69
+ raise ActionViewHelperError, error
70
+ when '503'
71
+ raise ActionViewHelperError, 'Service temporarily unavailable. Please try again.'
72
+ else
73
+ raise
74
+ end
75
+ end
76
+
77
+ end
78
+
79
+ class Notification < OffsitePayments::Notification
80
+ def initialize(post_arguments, options = {})
81
+ super
82
+
83
+ raise ArgumentError, "The transaction_id needs to be included in the query string." if transaction_id.nil?
84
+ raise ArgumentError, "The credential1 option needs to be set to the Mollie API key." if api_key.blank?
85
+ end
86
+
87
+ def complete?
88
+ true
89
+ end
90
+
91
+ def item_id
92
+ params['metadata']['order']
93
+ end
94
+
95
+ def transaction_id
96
+ params['id']
97
+ end
98
+
99
+ def api_key
100
+ @options[:credential1]
101
+ end
102
+
103
+ def currency
104
+ "EUR"
105
+ end
106
+
107
+ # the money amount we received in X.2 decimal.
108
+ def gross
109
+ @params['amount']
110
+ end
111
+
112
+ def gross_cents
113
+ (BigDecimal.new(@params['amount'], 2) * 100).to_i
114
+ end
115
+
116
+ def status
117
+ case @params['status']
118
+ when 'open'; 'Pending'
119
+ when 'paidout', 'paid'; 'Completed'
120
+ else 'Failed'
121
+ end
122
+ end
123
+
124
+ def test?
125
+ @params['mode'] == 'test'
126
+ end
127
+
128
+ def acknowledge(authcode = nil)
129
+ @params = check_payment_status(transaction_id)
130
+ true
131
+ end
132
+
133
+ def check_payment_status(transaction_id)
134
+ MollieMistercash.check_payment_status(@options[:credential1], transaction_id)
135
+ end
136
+ end
137
+
138
+ class Return < OffsitePayments::Return
139
+ end
140
+
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,193 @@
1
+ module OffsitePayments #:nodoc:
2
+ module Integrations #:nodoc:
3
+ module Molpay
4
+ mattr_accessor :acknowledge_url
5
+ self.acknowledge_url = 'https://www.onlinepayment.com.my/MOLPay/API/chkstat/returnipn.php'
6
+
7
+ def self.notification(post, options = {})
8
+ Notification.new(post, options)
9
+ end
10
+
11
+ def self.return(query_string, options={})
12
+ Return.new(query_string, options)
13
+ end
14
+
15
+ # (Optional Parameter) = channel //will generate URL to go directly to specific channel, e.g maybank2u, cimb
16
+ # Please refer MOLPay API spec for the channel routing
17
+ class Helper < OffsitePayments::Helper
18
+ include ActiveUtils::RequiresParameters
19
+
20
+ SUPPORTED_CURRENCIES = ['MYR', 'USD', 'SGD', 'PHP', 'VND', 'IDR', 'AUD', 'CNY', 'THB', 'GBP', 'EUR', 'HKD']
21
+
22
+ # Defaults to en
23
+ SUPPORTED_LANGUAGES = ['en', 'cn']
24
+
25
+ SERVICE_URL = 'https://www.onlinepayment.com.my/MOLPay/pay/'.freeze
26
+
27
+ mapping :account, 'merchantid'
28
+ mapping :amount, 'amount'
29
+ mapping :order, 'orderid'
30
+ mapping :customer, :name => 'bill_name',
31
+ :email => 'bill_email',
32
+ :phone => 'bill_mobile'
33
+
34
+ mapping :description, 'bill_desc'
35
+ mapping :language, 'langcode'
36
+ mapping :country, 'country'
37
+ mapping :currency, 'cur'
38
+ mapping :return_url, 'returnurl'
39
+ mapping :notify_url, 'callbackurl'
40
+ mapping :signature, 'vcode'
41
+
42
+ attr_reader :amount_in_cents, :verify_key, :channel
43
+
44
+ def credential_based_url
45
+ service_url = SERVICE_URL + @fields[mappings[:account]] + "/"
46
+ service_url = service_url + @channel if @channel
47
+ service_url
48
+ end
49
+
50
+ def initialize(order, account, options = {})
51
+ requires!(options, :amount, :currency, :credential2)
52
+ @verify_key = options[:credential2] if options[:credential2]
53
+ @amount_in_cents = options[:amount]
54
+ @channel = options.delete(:channel)
55
+ super
56
+ end
57
+
58
+ def form_fields
59
+ add_field mappings[:signature], signature
60
+ @fields
61
+ end
62
+
63
+ def amount=(money)
64
+ unless money > 0
65
+ raise ArgumentError, "amount must be greater than $0.00."
66
+ end
67
+ add_field mappings[:amount], sprintf("%.2f", money.to_f)
68
+ end
69
+
70
+ def currency=(cur)
71
+ raise ArgumentError, "unsupported currency" unless SUPPORTED_CURRENCIES.include?(cur)
72
+ add_field mappings[:currency], cur
73
+ end
74
+
75
+ def language=(lang)
76
+ raise ArgumentError, "unsupported language" unless SUPPORTED_LANGUAGES.include?(lang)
77
+ add_field mappings[:language], lang
78
+ end
79
+
80
+ private
81
+
82
+ def signature
83
+ Digest::MD5.hexdigest("#{@fields[mappings[:amount]]}#{@fields[mappings[:account]]}#{@fields[mappings[:order]]}#{@verify_key}")
84
+ end
85
+ end
86
+
87
+ class Notification < OffsitePayments::Notification
88
+ include ActiveUtils::PostsData
89
+
90
+ def status
91
+ case params['status']
92
+ when '00'
93
+ 'Completed'
94
+ when '11'
95
+ 'Failed'
96
+ when '22'
97
+ 'Pending'
98
+ end
99
+ end
100
+
101
+ def complete?
102
+ status == 'Completed'
103
+ end
104
+
105
+ def item_id
106
+ params['orderid']
107
+ end
108
+
109
+ def transaction_id
110
+ params['tranID']
111
+ end
112
+
113
+ def account
114
+ params["domain"]
115
+ end
116
+
117
+ # the money amount we received in X.2 decimal.
118
+ def gross
119
+ params['amount']
120
+ end
121
+
122
+ def currency
123
+ params['currency']
124
+ end
125
+
126
+ def channel
127
+ params['channel']
128
+ end
129
+
130
+ # When was this payment received by the client.
131
+ def received_at
132
+ params['paydate']
133
+ end
134
+
135
+ def auth_code
136
+ params['appcode']
137
+ end
138
+
139
+ def error_code
140
+ params['error_code']
141
+ end
142
+
143
+ def error_desc
144
+ params['error_desc']
145
+ end
146
+
147
+ def security_key
148
+ params['skey']
149
+ end
150
+
151
+ def test?
152
+ gross.blank? && auth_code.blank? && error_code.blank? && error_desc.blank? && security_key.blank?
153
+ end
154
+
155
+ def status_orig
156
+ params['status']
157
+ end
158
+
159
+ def acknowledge(authcode = nil)
160
+ payload = raw + '&treq=1'
161
+ ssl_post(Molpay.acknowledge_url, payload,
162
+ 'Content-Length' => "#{payload.size}",
163
+ 'User-Agent' => "Shopify/OffsitePayments"
164
+ )
165
+
166
+ status == 'Completed' && security_key == generate_signature
167
+ end
168
+
169
+ protected
170
+
171
+ def generate_signature
172
+ key0 = Digest::MD5.hexdigest("#{transaction_id}#{item_id}#{status_orig}#{account}#{gross}#{currency}")
173
+ Digest::MD5.hexdigest("#{received_at}#{account}#{key0}#{auth_code}#{@options[:credential2]}")
174
+ end
175
+ end
176
+
177
+ class Return < OffsitePayments::Return
178
+ def initialize(query_string, options = {})
179
+ super
180
+ @notification = Notification.new(query_string, options)
181
+ end
182
+
183
+ def success?
184
+ @notification.acknowledge
185
+ end
186
+
187
+ def pending?
188
+ @notification.status == 'Pending'
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
@@ -1,9 +1,9 @@
1
1
  module OffsitePayments #:nodoc:
2
2
  module Integrations #:nodoc:
3
- module Realex
3
+ module RealexOffsite
4
4
  mattr_accessor :production_url
5
5
  mattr_accessor :test_url
6
- self.production_url = 'https://hpp.realexpayments.com/pay'
6
+ self.production_url = 'https://epage.payandshop.com/epage.cgi'
7
7
  self.test_url = 'https://hpp.sandbox.realexpayments.com/pay'
8
8
 
9
9
  def self.helper(order, account, options={})
@@ -117,6 +117,8 @@ module OffsitePayments #:nodoc:
117
117
  # Realex does not send back CURRENCY param in response
118
118
  # however it does echo any other param so we send it twice.
119
119
  add_field 'X-CURRENCY', @currency
120
+ add_field 'X-TEST', @test.to_s
121
+ add_field 'ORDER_ID', "#{order}#{@timestamp.to_i}"
120
122
  end
121
123
 
122
124
  def form_fields
@@ -153,9 +155,10 @@ module OffsitePayments #:nodoc:
153
155
  # Realex Required Fields
154
156
  mapping :currency, 'CURRENCY'
155
157
 
156
- mapping :order, 'ORDER_ID'
158
+ mapping :order, 'CHECKOUT_ID'
157
159
  mapping :amount, 'AMOUNT'
158
- mapping :return_url, 'MERCHANT_RESPONSE_URL'
160
+ mapping :notify_url, 'MERCHANT_RESPONSE_URL'
161
+ mapping :return_url, 'MERCHANT_RETURN_URL'
159
162
 
160
163
  # Realex Optional fields
161
164
  mapping :customer, :email => 'CUST_NUM'
@@ -174,6 +177,22 @@ module OffsitePayments #:nodoc:
174
177
  end
175
178
 
176
179
  # Required Notification methods to define
180
+ def acknowledge(authcode = nil)
181
+ verified?
182
+ end
183
+
184
+ def item_id
185
+ checkout_id
186
+ end
187
+
188
+ def transaction_id
189
+ pasref
190
+ end
191
+
192
+ def test?
193
+ params['X-TEST']
194
+ end
195
+
177
196
  def status
178
197
  if result == '00'
179
198
  'Completed'
@@ -205,6 +224,10 @@ module OffsitePayments #:nodoc:
205
224
  params['MERCHANT_ID']
206
225
  end
207
226
 
227
+ def checkout_id
228
+ params['CHECKOUT_ID']
229
+ end
230
+
208
231
  def order_id
209
232
  params['ORDER_ID']
210
233
  end
@@ -69,14 +69,20 @@ module OffsitePayments #:nodoc:
69
69
  class Helper < OffsitePayments::Helper
70
70
  include Encryption
71
71
 
72
+ attr_reader :identifier
73
+
74
+ def initialize(order, account, options={})
75
+ super
76
+ @identifier = rand(0..99999).to_s.rjust(5, '0')
77
+ add_field 'VendorTxCode', "#{order}-#{@identifier}"
78
+ end
79
+
72
80
  mapping :credential2, 'EncryptKey'
73
81
 
74
82
  mapping :account, 'Vendor'
75
83
  mapping :amount, 'Amount'
76
84
  mapping :currency, 'Currency'
77
85
 
78
- mapping :order, 'VendorTxCode'
79
-
80
86
  mapping :customer,
81
87
  :first_name => 'BillingFirstnames',
82
88
  :last_name => 'BillingSurname',
@@ -246,7 +252,7 @@ module OffsitePayments #:nodoc:
246
252
 
247
253
  # Vendor-supplied code (:order mapping).
248
254
  def item_id
249
- params['VendorTxCode']
255
+ params['VendorTxCode'].rpartition('-').first
250
256
  end
251
257
 
252
258
  # Internal SagePay code, typically "{LONG-UUID}".
@@ -50,6 +50,10 @@ module OffsitePayments #:nodoc:
50
50
  @forward_url = options[:forward_url]
51
51
  @key = options[:credential2]
52
52
  @currency = options[:currency]
53
+
54
+ # x_credential3 should not be included in the request when using the universal offsite dev kit.
55
+ options[:credential3] = nil if options[:credential3] == @forward_url
56
+
53
57
  super
54
58
  self.country = options[:country]
55
59
  self.account_name = options[:account_name]
@@ -93,6 +97,8 @@ module OffsitePayments #:nodoc:
93
97
  mapping :transaction_type, 'x_transaction_type'
94
98
  mapping :description, 'x_description'
95
99
  mapping :invoice, 'x_invoice'
100
+ mapping :credential3, 'x_credential3'
101
+ mapping :credential4, 'x_credential4'
96
102
 
97
103
  mapping :customer, :first_name => 'x_customer_first_name',
98
104
  :last_name => 'x_customer_last_name',
@@ -1,3 +1,4 @@
1
+ require 'ipaddr'
1
2
  module OffsitePayments #:nodoc:
2
3
  module Integrations #:nodoc:
3
4
  module WorldPay
@@ -230,6 +231,12 @@ module OffsitePayments #:nodoc:
230
231
  return @custom_params ||= read_custom_params
231
232
  end
232
233
 
234
+ # Check if the request comes from IP range 195.35.90.0 – 195.35.91.255
235
+ def valid_sender?(ip)
236
+ return true if OffsitePayments.mode == :test
237
+ IPAddr.new("195.35.90.0/23").include?(IPAddr.new(ip))
238
+ end
239
+
233
240
  private
234
241
 
235
242
  # Take the posted data and move the relevant data into a hash
@@ -1,3 +1,3 @@
1
1
  module OffsitePayments
2
- VERSION = "2.1.0"
2
+ VERSION = "2.3.0"
3
3
  end
metadata CHANGED
@@ -1,36 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: offsite_payments
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Luetke
8
8
  autorequire:
9
9
  bindir: bin
10
- cert_chain:
11
- - |
12
- -----BEGIN CERTIFICATE-----
13
- MIIDcDCCAligAwIBAgIBATANBgkqhkiG9w0BAQUFADA/MQ8wDQYDVQQDDAZhZG1p
14
- bnMxFzAVBgoJkiaJk/IsZAEZFgdzaG9waWZ5MRMwEQYKCZImiZPyLGQBGRYDY29t
15
- MB4XDTE0MDUxNTIwMzM0OFoXDTE1MDUxNTIwMzM0OFowPzEPMA0GA1UEAwwGYWRt
16
- aW5zMRcwFQYKCZImiZPyLGQBGRYHc2hvcGlmeTETMBEGCgmSJomT8ixkARkWA2Nv
17
- bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL0/81O3e1vh5smcwp2G
18
- MpLQ6q0kejQLa65bPYPxdzWA1SYOKyGfw+yR9LdFzsuKpwWzKq6zX35lj1IckWS4
19
- bNBEQzxmufUxU0XPM02haFB8fOfDJzdXsWte9Ge4IFwahwn68gpMqN+BvxL+KMYz
20
- Iut9YmN44d4LZdsENEIO5vmybuG2vYDz7R56qB0PA+Q2P2CdhymsBad2DQs69FBo
21
- uico9V6VMYYctL9lCYdzu9IXrOYNTt88suKIVzzAlHOKeN0Ng5qdztFoTR8sfxDr
22
- Ydg3KHl5n47wlpgd8R0f/4b5gGxW+v9pyJCgQnLlRu7DedVSvv7+GMtj3g9r3nhJ
23
- KqECAwEAAaN3MHUwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFI/o
24
- maf34HXbUOQsdoLHacEKQgunMB0GA1UdEQQWMBSBEmFkbWluc0BzaG9waWZ5LmNv
25
- bTAdBgNVHRIEFjAUgRJhZG1pbnNAc2hvcGlmeS5jb20wDQYJKoZIhvcNAQEFBQAD
26
- ggEBADkK9aj5T0HPExsov4EoMWFnO+G7RQ28C30VAfKxnL2UxG6i4XMHVs6Xi94h
27
- qXFw1ec9Y2eDUqaolT3bviOk9BB197+A8Vz/k7MC6ci2NE+yDDB7HAC8zU6LAx8Y
28
- Iqvw7B/PSZ/pz4bUVFlTATif4mi1vO3lidRkdHRtM7UePSn2rUpOi0gtXBP3bLu5
29
- YjHJN7wx5cugMEyroKITG5gL0Nxtu21qtOlHX4Hc4KdE2JqzCPOsS4zsZGhgwhPs
30
- fl3hbtVFTqbOlwL9vy1fudXcolIE/ZTcxQ+er07ZFZdKCXayR9PPs64heamfn0fp
31
- TConQSX2BnZdhIEYW+cKzEC/bLc=
32
- -----END CERTIFICATE-----
33
- date: 2015-01-17 00:00:00.000000000 Z
10
+ cert_chain: []
11
+ date: 2016-02-08 00:00:00.000000000 Z
34
12
  dependencies:
35
13
  - !ruby/object:Gem::Dependency
36
14
  name: activesupport
@@ -41,7 +19,7 @@ dependencies:
41
19
  version: 3.2.14
42
20
  - - "<"
43
21
  - !ruby/object:Gem::Version
44
- version: 5.0.0
22
+ version: '5.1'
45
23
  type: :runtime
46
24
  prerelease: false
47
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -51,7 +29,7 @@ dependencies:
51
29
  version: 3.2.14
52
30
  - - "<"
53
31
  - !ruby/object:Gem::Version
54
- version: 5.0.0
32
+ version: '5.1'
55
33
  - !ruby/object:Gem::Dependency
56
34
  name: i18n
57
35
  requirement: !ruby/object:Gem::Requirement
@@ -70,9 +48,6 @@ dependencies:
70
48
  name: money
71
49
  requirement: !ruby/object:Gem::Requirement
72
50
  requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: 5.0.0
76
51
  - - "<"
77
52
  - !ruby/object:Gem::Version
78
53
  version: 7.0.0
@@ -80,9 +55,6 @@ dependencies:
80
55
  prerelease: false
81
56
  version_requirements: !ruby/object:Gem::Requirement
82
57
  requirements:
83
- - - ">="
84
- - !ruby/object:Gem::Version
85
- version: 5.0.0
86
58
  - - "<"
87
59
  - !ruby/object:Gem::Version
88
60
  version: 7.0.0
@@ -112,14 +84,14 @@ dependencies:
112
84
  requirements:
113
85
  - - "~>"
114
86
  - !ruby/object:Gem::Version
115
- version: 3.0.0
87
+ version: 3.2.0
116
88
  type: :runtime
117
89
  prerelease: false
118
90
  version_requirements: !ruby/object:Gem::Requirement
119
91
  requirements:
120
92
  - - "~>"
121
93
  - !ruby/object:Gem::Version
122
- version: 3.0.0
94
+ version: 3.2.0
123
95
  - !ruby/object:Gem::Dependency
124
96
  name: nokogiri
125
97
  requirement: !ruby/object:Gem::Requirement
@@ -143,7 +115,7 @@ dependencies:
143
115
  version: 3.2.20
144
116
  - - "<"
145
117
  - !ruby/object:Gem::Version
146
- version: 5.0.0
118
+ version: '5.1'
147
119
  type: :runtime
148
120
  prerelease: false
149
121
  version_requirements: !ruby/object:Gem::Requirement
@@ -153,7 +125,7 @@ dependencies:
153
125
  version: 3.2.20
154
126
  - - "<"
155
127
  - !ruby/object:Gem::Version
156
- version: 5.0.0
128
+ version: '5.1'
157
129
  - !ruby/object:Gem::Dependency
158
130
  name: rake
159
131
  requirement: !ruby/object:Gem::Requirement
@@ -262,7 +234,10 @@ files:
262
234
  - lib/offsite_payments/integrations/klarna.rb
263
235
  - lib/offsite_payments/integrations/liqpay.rb
264
236
  - lib/offsite_payments/integrations/maksuturva.rb
237
+ - lib/offsite_payments/integrations/mollie.rb
265
238
  - lib/offsite_payments/integrations/mollie_ideal.rb
239
+ - lib/offsite_payments/integrations/mollie_mistercash.rb
240
+ - lib/offsite_payments/integrations/molpay.rb
266
241
  - lib/offsite_payments/integrations/moneybookers.rb
267
242
  - lib/offsite_payments/integrations/nochex.rb
268
243
  - lib/offsite_payments/integrations/pag_seguro.rb
@@ -279,7 +254,7 @@ files:
279
254
  - lib/offsite_payments/integrations/pxpay.rb
280
255
  - lib/offsite_payments/integrations/quickpay.rb
281
256
  - lib/offsite_payments/integrations/rbkmoney.rb
282
- - lib/offsite_payments/integrations/realex.rb
257
+ - lib/offsite_payments/integrations/realex_offsite.rb
283
258
  - lib/offsite_payments/integrations/robokassa.rb
284
259
  - lib/offsite_payments/integrations/sage_pay_form.rb
285
260
  - lib/offsite_payments/integrations/two_checkout.rb
@@ -293,7 +268,7 @@ files:
293
268
  - lib/offsite_payments/notification.rb
294
269
  - lib/offsite_payments/return.rb
295
270
  - lib/offsite_payments/version.rb
296
- homepage: https://github.com/Shopify/offsite_payments
271
+ homepage: https://github.com/activemerchant/offsite_payments
297
272
  licenses:
298
273
  - MIT
299
274
  metadata: {}
@@ -313,7 +288,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
313
288
  version: '0'
314
289
  requirements: []
315
290
  rubyforge_project:
316
- rubygems_version: 2.2.2
291
+ rubygems_version: 2.2.3
317
292
  signing_key:
318
293
  specification_version: 4
319
294
  summary: Framework and tools for dealing with offsite (hosted) payment pages.
Binary file
data.tar.gz.sig DELETED
Binary file
metadata.gz.sig DELETED
Binary file