offsite_payments 2.1.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  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