rails-gp-webpay 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +41 -0
  3. data/Rakefile +6 -0
  4. data/app/controllers/gp_webpay/cards_controller.rb +11 -0
  5. data/app/controllers/gp_webpay/orders_controller.rb +11 -0
  6. data/app/controllers/gp_webpay_controller.rb +18 -0
  7. data/changelog.md +4 -0
  8. data/config/keys/cert.pem +8 -0
  9. data/config/keys/pkey.pem +13 -0
  10. data/config/routes.rb +4 -0
  11. data/config/wsdl/GPwebpayAdditionalInfoResponse_v1.xsd +194 -0
  12. data/config/wsdl/cws_v1.wsdl +2355 -0
  13. data/config/wsdl/swaref.xsd +59 -0
  14. data/lib/gp_webpay.rb +55 -0
  15. data/lib/gp_webpay/configuration.rb +65 -0
  16. data/lib/gp_webpay/engine.rb +7 -0
  17. data/lib/gp_webpay/error.rb +4 -0
  18. data/lib/gp_webpay/http/base_signed_request.rb +72 -0
  19. data/lib/gp_webpay/http/create_order.rb +24 -0
  20. data/lib/gp_webpay/http/external_url.rb +13 -0
  21. data/lib/gp_webpay/http/http_request.rb +63 -0
  22. data/lib/gp_webpay/http/http_response.rb +40 -0
  23. data/lib/gp_webpay/http/validate_result.rb +63 -0
  24. data/lib/gp_webpay/http/verify_card.rb +18 -0
  25. data/lib/gp_webpay/openssl_security.rb +16 -0
  26. data/lib/gp_webpay/response.rb +34 -0
  27. data/lib/gp_webpay/service.rb +15 -0
  28. data/lib/gp_webpay/version.rb +3 -0
  29. data/lib/gp_webpay/ws/base_signed_request.rb +69 -0
  30. data/lib/gp_webpay/ws/echo.rb +35 -0
  31. data/lib/gp_webpay/ws/services/get_master_payment_status.rb +32 -0
  32. data/lib/gp_webpay/ws/services/get_payment_status.rb +19 -0
  33. data/lib/gp_webpay/ws/services/get_token_status.rb +21 -0
  34. data/lib/gp_webpay/ws/services/process_cancel_capture.rb +20 -0
  35. data/lib/gp_webpay/ws/services/process_capture_reverse.rb +20 -0
  36. data/lib/gp_webpay/ws/services/process_card_on_file_payment.rb +40 -0
  37. data/lib/gp_webpay/ws/services/process_master_payment_revoke.rb +20 -0
  38. data/lib/gp_webpay/ws/services/process_recurring_payment.rb +20 -0
  39. data/lib/gp_webpay/ws/services/process_refund_payment.rb +20 -0
  40. data/lib/gp_webpay/ws/services/process_token_payment.rb +26 -0
  41. data/lib/gp_webpay/ws/services/process_token_revoke.rb +20 -0
  42. data/lib/gp_webpay/ws/services/process_usage_based_payment.rb +21 -0
  43. data/lib/gp_webpay/ws/validate_result.rb +48 -0
  44. data/lib/gp_webpay/ws/ws_request.rb +39 -0
  45. data/lib/gp_webpay/ws/ws_response.rb +54 -0
  46. metadata +135 -0
@@ -0,0 +1,59 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ Copyright (c) 2002-2004 by The Web Services-Interoperability Organization (WS-I) and
4
+ Certain of its Members. All Rights Reserved.
5
+
6
+ Notice
7
+ The material contained herein is not a license, either expressly or impliedly, to any
8
+ intellectual property owned or controlled by any of the authors or developers of this
9
+ material or WS-I. The material contained herein is provided on an "AS IS" basis and to
10
+ the maximum extent permitted by applicable law, this material is provided AS IS AND WITH
11
+ ALL FAULTS, and the authors and developers of this material and WS-I hereby disclaim all
12
+ other warranties and conditions, either express, implied or statutory, including, but not
13
+ limited to, any (if any) implied warranties, duties or conditions of merchantability,
14
+ of fitness for a particular purpose, of accuracy or completeness of responses, of results,
15
+ of workmanlike effort, of lack of viruses, and of lack of negligence. ALSO, THERE IS NO
16
+ WARRANTY OR CONDITION OF TITLE, QUIET ENJOYMENT, QUIET POSSESSION, CORRESPONDENCE TO
17
+ DESCRIPTION OR NON-INFRINGEMENT WITH REGARD TO THIS MATERIAL.
18
+
19
+ IN NO EVENT WILL ANY AUTHOR OR DEVELOPER OF THIS MATERIAL OR WS-I BE LIABLE TO ANY OTHER
20
+ PARTY FOR THE COST OF PROCURING SUBSTITUTE GOODS OR SERVICES, LOST PROFITS, LOSS OF USE,
21
+ LOSS OF DATA, OR ANY INCIDENTAL, CONSEQUENTIAL, DIRECT, INDIRECT, OR SPECIAL DAMAGES
22
+ WHETHER UNDER CONTRACT, TORT, WARRANTY, OR OTHERWISE, ARISING IN ANY WAY OUT OF THIS OR
23
+ ANY OTHER AGREEMENT RELATING TO THIS MATERIAL, WHETHER OR NOT SUCH PARTY HAD ADVANCE
24
+ NOTICE OF THE POSSIBILITY OF SUCH DAMAGES.
25
+
26
+ WS-I License Information
27
+ Use of this WS-I Material is governed by the WS-I Test License and other licenses. Information on these
28
+ licenses are contained in the README.txt and ReleaseNotes.txt files. By downloading this file, you agree
29
+ to the terms of these licenses.
30
+
31
+ How To Provide Feedback
32
+ The Web Services-Interoperability Organization (WS-I) would like to receive input,
33
+ suggestions and other feedback ("Feedback") on this work from a wide variety of
34
+ industry participants to improve its quality over time.
35
+
36
+ By sending email, or otherwise communicating with WS-I, you (on behalf of yourself if
37
+ you are an individual, and your company if you are providing Feedback on behalf of the
38
+ company) will be deemed to have granted to WS-I, the members of WS-I, and other parties
39
+ that have access to your Feedback, a non-exclusive, non-transferable, worldwide, perpetual,
40
+ irrevocable, royalty-free license to use, disclose, copy, license, modify, sublicense or
41
+ otherwise distribute and exploit in any manner whatsoever the Feedback you provide regarding
42
+ the work. You acknowledge that you have no expectation of confidentiality with respect to
43
+ any Feedback you provide. You represent and warrant that you have rights to provide this
44
+ Feedback, and if you are providing Feedback on behalf of a company, you represent and warrant
45
+ that you have the rights to provide Feedback on behalf of your company. You also acknowledge
46
+ that WS-I is not required to review, discuss, use, consider or in any way incorporate your
47
+ Feedback into future versions of its work. If WS-I does incorporate some or all of your
48
+ Feedback in a future version of the work, it may, but is not obligated to include your name
49
+ (or, if you are identified as acting on behalf of your company, the name of your company) on
50
+ a list of contributors to the work. If the foregoing is not acceptable to you and any company
51
+ on whose behalf you are acting, please do not provide any Feedback.
52
+
53
+ Feedback on this document should be directed to wsi-test-comments@ws-i.org.
54
+ -->
55
+ <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://ws-i.org/profiles/basic/1.1/xsd" xmlns="http://ws-i.org/profiles/basic/1.1/xsd">
56
+ <xsd:simpleType name="swaRef">
57
+ <xsd:restriction base="xsd:anyURI"/>
58
+ </xsd:simpleType>
59
+ </xsd:schema>
data/lib/gp_webpay.rb ADDED
@@ -0,0 +1,55 @@
1
+ require 'nokogiri'
2
+ require 'savon'
3
+ require 'active_support/core_ext/hash'
4
+ require 'active_support/core_ext/object'
5
+
6
+ require 'gp_webpay/error'
7
+ require 'gp_webpay/version'
8
+ require 'gp_webpay/engine'
9
+ require 'gp_webpay/service'
10
+ require 'gp_webpay/openssl_security'
11
+ require 'gp_webpay/configuration'
12
+ require 'gp_webpay/response'
13
+
14
+ require 'gp_webpay/http/base_signed_request'
15
+ require 'gp_webpay/http/external_url'
16
+ require 'gp_webpay/http/http_response'
17
+ require 'gp_webpay/http/http_request'
18
+ require 'gp_webpay/http/create_order'
19
+ require 'gp_webpay/http/verify_card'
20
+ require 'gp_webpay/http/validate_result'
21
+
22
+ require 'gp_webpay/ws/ws_request'
23
+ require 'gp_webpay/ws/validate_result'
24
+ require 'gp_webpay/ws/ws_response'
25
+ require 'gp_webpay/ws/base_signed_request'
26
+ require 'gp_webpay/ws/echo'
27
+
28
+ require 'gp_webpay/ws/services/get_master_payment_status'
29
+ require 'gp_webpay/ws/services/get_payment_status'
30
+ require 'gp_webpay/ws/services/get_token_status'
31
+ require 'gp_webpay/ws/services/process_cancel_capture'
32
+ require 'gp_webpay/ws/services/process_capture_reverse'
33
+ require 'gp_webpay/ws/services/process_card_on_file_payment'
34
+ require 'gp_webpay/ws/services/process_master_payment_revoke'
35
+ require 'gp_webpay/ws/services/process_recurring_payment'
36
+ require 'gp_webpay/ws/services/process_refund_payment'
37
+ require 'gp_webpay/ws/services/process_token_payment'
38
+ require 'gp_webpay/ws/services/process_token_revoke'
39
+ require 'gp_webpay/ws/services/process_usage_based_payment'
40
+
41
+ module GpWebpay
42
+ @configuration = Configuration.new
43
+
44
+ def self.config
45
+ @configuration
46
+ end
47
+
48
+ def self.configure
49
+ yield(@configuration)
50
+ end
51
+
52
+ def self.root
53
+ File.dirname(__dir__)
54
+ end
55
+ end
@@ -0,0 +1,65 @@
1
+ module GpWebpay
2
+ class Configuration
3
+ attr_accessor :configurations, :parent_controller, :mount_at, :orders_controller, :cards_controller
4
+
5
+ def initialize
6
+ @configurations = {}
7
+ @parent_controller = 'AbstractController::Base'
8
+ @mount_at = '/gp_webpay'
9
+ @orders_controller = 'OrdersController'
10
+ @cards_controller = 'CardsController'
11
+ end
12
+
13
+ def default
14
+ @configurations[:default]
15
+ end
16
+
17
+ def [](config_name)
18
+ @configurations[config_name]
19
+ end
20
+
21
+ def add_configuration(merchant_number:, default: false)
22
+ @configurations[merchant_number] = MerchantConfig.new(merchant_number)
23
+ yield(@configurations[merchant_number])
24
+ @configurations[:default] = @configurations[merchant_number] if default || !@configurations[:default]
25
+ end
26
+
27
+ def remove_configuration(merchant_number:)
28
+ @configurations[merchant_number] = nil
29
+ @configurations[:default] = @configurations[@configurations.keys[0]]
30
+ end
31
+
32
+ class MerchantConfig
33
+ attr_accessor :merchant_number, :merchant_pem, :merchant_password, :gpe_pem, :wsdl_file, :provider, :enabled_methods, :production
34
+ attr_writer :http_url, :ws_url
35
+
36
+ DEFAULT_HTTP_URL = 'https://3dsecure.gpwebpay.com/pgw/order.do'.freeze
37
+ DEFAULT_HTTP_TEST_URL = 'https://test.3dsecure.gpwebpay.com/pgw/order.do'.freeze
38
+ DEFAULT_WS_URL = 'https://3dsecure.gpwebpay.com/pay-ws/v1/PaymentService'.freeze
39
+ DEFAULT_WS_TEST_URL = 'https://test.3dsecure.gpwebpay.com/pay-ws/v1/PaymentService'.freeze
40
+
41
+ def initialize(merchant_number)
42
+ @merchant_number = merchant_number
43
+ @production = false
44
+ @wsdl_file = File.read("#{GpWebpay.root}/config/wsdl/cws_v1.wsdl")
45
+ @enabled_methods = 'credit_card,transfer'
46
+ end
47
+
48
+ def http_url
49
+ if @http_url.nil?
50
+ production ? DEFAULT_HTTP_URL : DEFAULT_HTTP_TEST_URL
51
+ else
52
+ @http_url
53
+ end
54
+ end
55
+
56
+ def ws_url
57
+ if @ws_url.nil?
58
+ production ? DEFAULT_WS_URL : DEFAULT_WS_TEST_URL
59
+ else
60
+ @ws_url
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,7 @@
1
+ if Object.const_defined?(:Rails)
2
+ module GpWebpay
3
+ class Engine < ::Rails::Engine
4
+ isolate_namespace GpWebpay
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ module GpWebpay
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -0,0 +1,72 @@
1
+ ##
2
+ # Service object signs request which can be send to GP Webpay payment gateway.
3
+ #
4
+ # 1. Use request value object to translate attributes to correct GP Webpay format and order.
5
+ # 2. Append generated digest to attributes.
6
+ # 3. prepare url to send for GP Webpay
7
+ #
8
+ # @param [Hash] optional attributes for GP Webpay
9
+ #
10
+ # @return [Hash] data needed to make request:
11
+ # full url for GET request or
12
+ # url_root and list of attributes for POST request.
13
+
14
+ module GpWebpay
15
+ module Http
16
+ class BaseSignedRequest < Service
17
+ attr_reader :attributes, :locale, :operation, :config, :url_attributes
18
+
19
+ def initialize(attributes, locale, operation, merchant_number: :default, url_attributes: {})
20
+ super()
21
+ @attributes = attributes
22
+ @locale = locale
23
+ @merchant_number = merchant_number
24
+ @operation = operation
25
+ @url_attributes = url_attributes
26
+ @config = GpWebpay.config[@merchant_number] || GpWebpay.config.default
27
+ end
28
+
29
+ def call
30
+ request = HttpRequest.new(
31
+ attributes.merge(
32
+ merchant_number: @config.merchant_number,
33
+ operation: operation,
34
+ url: callback_url
35
+ )
36
+ ).to_gpwebpay
37
+
38
+ attrs_with_digest = payment_attributes_with_digest(request)
39
+ uri = URI(@config.http_url)
40
+
41
+ ExternalUrl.new(
42
+ url: uri.to_s,
43
+ full_url: build_full_url(uri, attrs_with_digest),
44
+ params: attrs_with_digest
45
+ )
46
+ end
47
+
48
+ def callback_url
49
+ raise NotImplementedError
50
+ end
51
+
52
+ private
53
+
54
+ def build_full_url(uri, attrs)
55
+ uri.query = URI.encode_www_form(attrs)
56
+ uri.to_s
57
+ end
58
+
59
+ def digest_text(attrs)
60
+ attrs.values.join('|')
61
+ end
62
+
63
+ def payment_attributes_with_digest(attrs)
64
+ digest = OpensslSecurity.generate_digest(@config, digest_text(attrs))
65
+ attrs.merge(
66
+ 'DIGEST' => digest,
67
+ 'LANG' => locale
68
+ )
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,24 @@
1
+ ##
2
+ # Service object creates request data for GP Webpay CREATE_ORDER operation.
3
+ #
4
+ # Examples:
5
+ # => Create order which will remember credit card and send back TOKEN.
6
+ # > GpWebpay::Http::CreateOrder.call(order_number: 146, amount: 456, currency: 978, deposit_flag: 1, user_param1: 'T')
7
+ # => Use returned token to skip adding card again:
8
+ # > GpWebpay::Http::CreateOrder.call(order_number: 147, amount: 456, currency: 978, deposit_flag: 1, user_param1: 'S', token: 'TOKEN')
9
+
10
+ module GpWebpay
11
+ module Http
12
+ class CreateOrder < BaseSignedRequest
13
+ def initialize(attributes, locale, merchant_number: nil, url_attributes: {})
14
+ super(attributes, locale, 'CREATE_ORDER', merchant_number: merchant_number, url_attributes: url_attributes)
15
+ end
16
+
17
+ protected
18
+
19
+ def callback_url
20
+ GpWebpay::Engine.routes.url_helpers.gp_webpay_orders_path({ merchant_number: config.merchant_number, locale: locale }.merge(url_attributes))
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,13 @@
1
+ module GpWebpay
2
+ module Http
3
+ class ExternalUrl
4
+ attr_accessor :url, :full_url, :params
5
+
6
+ def initialize(url:, full_url:, params:)
7
+ @url = url
8
+ @full_url = full_url
9
+ @params = params
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,63 @@
1
+ module GpWebpay
2
+ module Http
3
+ class HttpRequest
4
+ attr_accessor :attributes
5
+
6
+ ATTRS_TO_GP_MAPPER = {
7
+ 'MERCHANTNUMBER' => :merchant_number,
8
+ 'OPERATION' => :operation,
9
+ 'ORDERNUMBER' => :order_number,
10
+ 'AMOUNT' => :amount,
11
+ 'CURRENCY' => :currency,
12
+ 'DEPOSITFLAG' => :deposit_flag,
13
+ 'MERORDERNUM' => :mer_order_num,
14
+ 'URL' => :url,
15
+ 'DESCRIPTION' => :description,
16
+ 'MD' => :md,
17
+ 'USERPARAM1' => :user_param1,
18
+ 'FASTPAYID' => :fast_pay_id,
19
+ 'PAYMETHOD' => :paymethod,
20
+ 'DISABLEPAYMETHOD' => :disable_paymethod,
21
+ 'PAYMETHODS' => :paymethods,
22
+ 'EMAIL' => :email,
23
+ 'REFERENCENUMBER' => :reference_number,
24
+ 'ADDINFO' => :add_info_to_xml,
25
+ 'FASTTOKEN' => :fast_token
26
+ }.freeze
27
+
28
+ def initialize(attributes)
29
+ @attributes = attributes || {}
30
+ end
31
+
32
+ def to_gpwebpay
33
+ @to_gpwebpay ||= transform_to_gpwebpay
34
+ end
35
+
36
+ private
37
+
38
+ def transform_to_gpwebpay
39
+ result = ATTRS_TO_GP_MAPPER.each_with_object({}) do |(k, v), attrs|
40
+ attribute_value = attributes[v] || attributes[v.to_s]
41
+ attrs[k] = attribute_value if attribute_value
42
+ end
43
+ result = result.merge({ 'ADDINFO' => add_info_to_xml }) if attributes[:add_info] || attributes['add_info']
44
+ result
45
+ end
46
+
47
+ def add_info_to_xml
48
+ builder = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
49
+ xml.additionalInfoRequest(xmlns: 'http://gpe.cz/gpwebpay/additionalInfo/request',
50
+ 'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
51
+ version: 4.0) do
52
+ xml.requestReturnInfo do
53
+ xml.requestCardsDetails true
54
+ end
55
+ end
56
+ end
57
+ builder.to_xml(
58
+ save_with: Nokogiri::XML::Node::SaveOptions::AS_XML
59
+ ).strip.gsub("\n", '')
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,40 @@
1
+ module GpWebpay
2
+ module Http
3
+ class HttpResponse < Response
4
+ GP_TO_ATTRS_MAPPER =
5
+ {
6
+ 'OPERATION' => :operation,
7
+ 'ORDERNUMBER' => :order_number,
8
+ 'MERORDERNUM' => :mer_order_num,
9
+ 'MD' => :md,
10
+ 'PRCODE' => :pr_code,
11
+ 'SRCODE' => :sr_code,
12
+ 'RESULTTEXT' => :result_text,
13
+ 'USERPARAM1' => :user_param1,
14
+ 'TOKEN' => :token,
15
+ 'EXPIRY' => :expiry,
16
+ 'ACSRES' => :acsres,
17
+ 'ACCODE' => :accode,
18
+ 'PANPATTERN' => :pan_pattern,
19
+ 'DAYTOCAPTURE' => :day_to_capture,
20
+ 'TOKENREGSTATUS' => :token_reg_status
21
+ }.freeze
22
+
23
+ def self.from_hash(hash, merchant_number)
24
+ params = GP_TO_ATTRS_MAPPER.each_with_object({}) do |(k, v), result|
25
+ value = hash[k.to_s] || hash[k.to_sym]
26
+ result[v] = value if value
27
+ end
28
+
29
+ params[:add_info] = Hash.from_xml(hash['ADDINFO']) if hash['ADDINFO']
30
+
31
+ new(original_response: hash, result_text: params[:result_text], token: params[:token], status: nil,
32
+ pr_code: params[:pr_code], sr_code: params[:sr_code], params: params, merchant_number: merchant_number)
33
+ end
34
+
35
+ def valid?
36
+ GpWebpay::Http::ValidateResult.call(original_response, config)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,63 @@
1
+ ##
2
+ # Service object validates result received from GP Webpay response redirect.
3
+ #
4
+ # 1. Use public cert of GP Webpay to verify it comes from GP Webpay.
5
+ # 2. Whitelist allowed attributes which are expected from GP Webpay.
6
+ # 3. Calculate digest and make sure it corresponds to received DIGEST.
7
+ # 3. Calculate digest1 and make sure it corresponds to received DIGEST1
8
+ # (which includes our merchant number for extra security).
9
+ #
10
+ # @param [Hash] Parameters hash received in response from GP Webpay.
11
+ #
12
+ # @return [Boolean] true if signature is valid for both digests.
13
+
14
+ module GpWebpay
15
+ module Http
16
+ class ValidateResult < Service
17
+ attr_reader :params, :config
18
+
19
+ def initialize(params, config)
20
+ super()
21
+ @params = params
22
+ @config = config
23
+ end
24
+
25
+ def call
26
+ params['DIGEST'] && params['DIGEST1'] && OpensslSecurity.validate_digests(
27
+ config,
28
+ params['DIGEST'] => digest_verification,
29
+ params['DIGEST1'] => digest1_verification
30
+ )
31
+ end
32
+
33
+ private
34
+
35
+ DIGEST_ALLOWED_ATTRIBUTES = %w[
36
+ OPERATION
37
+ ORDERNUMBER
38
+ MERORDERNUM
39
+ MD
40
+ PRCODE
41
+ SRCODE
42
+ RESULTTEXT
43
+ USERPARAM1
44
+ ADDINFO
45
+ TOKEN
46
+ EXPIRY
47
+ ACSRES
48
+ ACCODE
49
+ PANPATTERN
50
+ DAYTOCAPTURE
51
+ TOKENREGSTATUS
52
+ ].freeze
53
+
54
+ def digest_verification
55
+ @digest_verification ||= (DIGEST_ALLOWED_ATTRIBUTES & params.keys).map { |key| params[key] }.join('|')
56
+ end
57
+
58
+ def digest1_verification
59
+ "#{digest_verification}|#{config.merchant_number}"
60
+ end
61
+ end
62
+ end
63
+ end