killbill-paypal-express 1.7.0 → 1.7.1

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -4
  3. data/Gemfile +1 -1
  4. data/Jarfile +7 -5
  5. data/LICENSE +201 -0
  6. data/README.md +1 -1
  7. data/Rakefile +2 -2
  8. data/VERSION +1 -1
  9. data/config.ru +1 -1
  10. data/db/ddl.sql +35 -7
  11. data/db/schema.rb +46 -18
  12. data/killbill-paypal-express.gemspec +13 -8
  13. data/lib/paypal_express.rb +10 -142
  14. data/lib/paypal_express/api.rb +177 -180
  15. data/lib/paypal_express/application.rb +101 -0
  16. data/lib/paypal_express/models/payment_method.rb +32 -0
  17. data/lib/paypal_express/models/response.rb +109 -0
  18. data/lib/paypal_express/models/transaction.rb +11 -0
  19. data/lib/paypal_express/private_api.rb +30 -43
  20. data/lib/paypal_express/views/form.erb +8 -0
  21. data/paypal_express.yml +22 -0
  22. data/pom.xml +4 -3
  23. data/release.sh +8 -8
  24. data/spec/paypal_express/base_plugin_spec.rb +29 -56
  25. data/spec/paypal_express/remote/integration_spec.rb +117 -136
  26. data/spec/spec_helper.rb +1 -14
  27. metadata +92 -29
  28. data/lib/paypal_express/config/application.rb +0 -55
  29. data/lib/paypal_express/config/configuration.rb +0 -50
  30. data/lib/paypal_express/config/properties.rb +0 -27
  31. data/lib/paypal_express/models/paypal_express_payment_method.rb +0 -140
  32. data/lib/paypal_express/models/paypal_express_response.rb +0 -235
  33. data/lib/paypal_express/models/paypal_express_transaction.rb +0 -48
  34. data/lib/paypal_express/paypal/gateway.rb +0 -26
  35. data/lib/paypal_express/paypal_express_utils.rb +0 -43
  36. data/spec/paypal_express/paypal_express_payment_method_spec.rb +0 -130
  37. data/spec/paypal_express/paypal_express_response_spec.rb +0 -84
  38. data/spec/paypal_express/streamy_result_set_spec.rb +0 -37
@@ -3,7 +3,7 @@ version = File.read(File.expand_path('../VERSION', __FILE__)).strip
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'killbill-paypal-express'
5
5
  s.version = version
6
- s.summary = 'Plugin to use Express Checkout as a gateway.'
6
+ s.summary = 'Plugin to use Paypal Express Checkout as a gateway.'
7
7
  s.description = 'Kill Bill payment plugin for Paypal Express Checkout.'
8
8
 
9
9
  s.required_ruby_version = '>= 1.9.3'
@@ -18,17 +18,22 @@ Gem::Specification.new do |s|
18
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
19
  s.bindir = 'bin'
20
20
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
- s.require_paths = ["lib"]
21
+ s.require_paths = ['lib']
22
22
 
23
23
  s.rdoc_options << '--exclude' << '.'
24
24
 
25
- s.add_dependency 'killbill', '~> 3.0.0'
26
- s.add_dependency 'activemerchant', '~> 1.36.0'
27
- s.add_dependency 'activerecord', '~> 3.2.1'
28
- s.add_dependency 'money', '~> 6.0.0'
25
+ s.add_dependency 'killbill', '~> 3.1.8'
26
+ s.add_dependency 'activemerchant', '~> 1.44.1'
27
+ s.add_dependency 'offsite_payments', '~> 2.0.1'
28
+ s.add_dependency 'activerecord', '~> 4.1.0'
29
+ s.add_dependency 'actionpack', '~> 4.1.0'
30
+ s.add_dependency 'actionview', '~> 4.1.0'
31
+ s.add_dependency 'activesupport', '~> 4.1.0'
32
+ s.add_dependency 'money', '~> 6.1.1'
33
+ s.add_dependency 'monetize', '~> 0.3.0'
29
34
  s.add_dependency 'sinatra', '~> 1.3.4'
30
35
  if defined?(JRUBY_VERSION)
31
- s.add_dependency 'activerecord-jdbcmysql-adapter', '~> 1.2.9'
36
+ s.add_dependency 'activerecord-jdbcmysql-adapter', '~> 1.3.7'
32
37
  # Required to avoid errors like java.lang.NoClassDefFoundError: org/bouncycastle/asn1/DERBoolean
33
38
  s.add_dependency 'jruby-openssl', '~> 0.9.4'
34
39
  end
@@ -37,7 +42,7 @@ Gem::Specification.new do |s|
37
42
  s.add_development_dependency 'rake', '>= 10.0.0'
38
43
  s.add_development_dependency 'rspec', '~> 2.12.0'
39
44
  if defined?(JRUBY_VERSION)
40
- s.add_development_dependency 'activerecord-jdbcsqlite3-adapter', '~> 1.2.6'
45
+ s.add_development_dependency 'activerecord-jdbcsqlite3-adapter', '~> 1.3.7'
41
46
  else
42
47
  s.add_development_dependency 'sqlite3', '~> 1.3.7'
43
48
  end
@@ -1,156 +1,24 @@
1
+ require 'action_controller'
1
2
  require 'active_record'
2
- require 'activemerchant'
3
+ require 'action_view'
4
+ require 'active_merchant'
5
+ require 'active_support'
3
6
  require 'bigdecimal'
4
7
  require 'money'
8
+ require 'monetize'
9
+ require 'offsite_payments'
5
10
  require 'pathname'
6
11
  require 'sinatra'
7
12
  require 'singleton'
8
13
  require 'yaml'
9
14
 
10
15
  require 'killbill'
11
-
12
- require 'paypal_express/config/configuration'
13
- require 'paypal_express/config/properties'
14
-
15
- require 'paypal_express/paypal/gateway'
16
-
17
- require 'paypal_express/models/paypal_express_payment_method'
18
- require 'paypal_express/models/paypal_express_response'
19
- require 'paypal_express/models/paypal_express_transaction'
20
-
21
- require 'paypal_express/paypal_express_utils'
16
+ require 'killbill/helpers/active_merchant'
22
17
 
23
18
  require 'paypal_express/api'
24
19
  require 'paypal_express/private_api'
25
20
 
21
+ require 'paypal_express/models/payment_method'
22
+ require 'paypal_express/models/response'
23
+ require 'paypal_express/models/transaction'
26
24
 
27
- # Thank you Rails for the following!
28
-
29
- class Object
30
- def blank?
31
- respond_to?(:empty?) ? empty? : !self
32
- end
33
- end
34
-
35
- class Hash
36
- # Return a new hash with all keys converted to symbols, as long as
37
- # they respond to +to_sym+. This includes the keys from the root hash
38
- # and from all nested hashes.
39
- #
40
- # hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
41
- #
42
- # hash.deep_symbolize_keys
43
- # # => { person: { name: "Rob", age: "28" } }
44
- def deep_symbolize_keys
45
- deep_transform_keys { |key| key.to_sym rescue key }
46
- end
47
-
48
- # Return a new hash with all keys converted by the block operation.
49
- # This includes the keys from the root hash and from all
50
- # nested hashes.
51
- #
52
- # hash = { person: { name: 'Rob', age: '28' } }
53
- #
54
- # hash.deep_transform_keys{ |key| key.to_s.upcase }
55
- # # => { "PERSON" => { "NAME" => "Rob", "AGE" => "28" } }
56
- def deep_transform_keys(&block)
57
- result = {}
58
- each do |key, value|
59
- result[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys(&block) : value
60
- end
61
- result
62
- end
63
-
64
- # By default, only instances of Hash itself are extractable.
65
- # Subclasses of Hash may implement this method and return
66
- # true to declare themselves as extractable. If a Hash
67
- # is extractable, Array#extract_options! pops it from
68
- # the Array when it is the last element of the Array.
69
- def extractable_options?
70
- instance_of?(Hash)
71
- end
72
- end
73
-
74
- class Array
75
- # Extracts options from a set of arguments. Removes and returns the last
76
- # element in the array if it's a hash, otherwise returns a blank hash.
77
- #
78
- # def options(*args)
79
- # args.extract_options!
80
- # end
81
- #
82
- # options(1, 2) # => {}
83
- # options(1, 2, a: :b) # => {:a=>:b}
84
- def extract_options!
85
- if last.is_a?(Hash) && last.extractable_options?
86
- pop
87
- else
88
- {}
89
- end
90
- end
91
- end
92
-
93
- class Module
94
- def mattr_reader(*syms)
95
- options = syms.extract_options!
96
- syms.each do |sym|
97
- raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/
98
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
99
- @@#{sym} = nil unless defined? @@#{sym}
100
-
101
- def self.#{sym}
102
- @@#{sym}
103
- end
104
- EOS
105
-
106
- unless options[:instance_reader] == false || options[:instance_accessor] == false
107
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
108
- def #{sym}
109
- @@#{sym}
110
- end
111
- EOS
112
- end
113
- end
114
- end
115
-
116
- def mattr_writer(*syms)
117
- options = syms.extract_options!
118
- syms.each do |sym|
119
- raise NameError.new('invalid attribute name') unless sym =~ /^[_A-Za-z]\w*$/
120
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
121
- def self.#{sym}=(obj)
122
- @@#{sym} = obj
123
- end
124
- EOS
125
-
126
- unless options[:instance_writer] == false || options[:instance_accessor] == false
127
- class_eval(<<-EOS, __FILE__, __LINE__ + 1)
128
- def #{sym}=(obj)
129
- @@#{sym} = obj
130
- end
131
- EOS
132
- end
133
- end
134
- end
135
-
136
- # Extends the module object with module and instance accessors for class attributes,
137
- # just like the native attr* accessors for instance attributes.
138
- #
139
- # module AppConfiguration
140
- # mattr_accessor :google_api_key
141
- #
142
- # self.google_api_key = "123456789"
143
- # end
144
- #
145
- # AppConfiguration.google_api_key # => "123456789"
146
- # AppConfiguration.google_api_key = "overriding the api key!"
147
- # AppConfiguration.google_api_key # => "overriding the api key!"
148
- #
149
- # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
150
- # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
151
- # To opt out of both instance methods, pass <tt>instance_accessor: false</tt>.
152
- def mattr_accessor(*syms)
153
- mattr_reader(*syms)
154
- mattr_writer(*syms)
155
- end
156
- end
@@ -1,235 +1,232 @@
1
- module Killbill::PaypalExpress
2
- class PaymentPlugin < Killbill::Plugin::Payment
3
- def start_plugin
4
- Killbill::PaypalExpress.initialize! @logger, @conf_dir
5
- @gateway = Killbill::PaypalExpress.gateway
1
+ module Killbill #:nodoc:
2
+ module PaypalExpress #:nodoc:
3
+ class PaymentPlugin < ::Killbill::Plugin::ActiveMerchant::PaymentPlugin
4
+
5
+ def initialize
6
+ gateway_builder = Proc.new do |config|
7
+ ::ActiveMerchant::Billing::PaypalExpressGateway.new :signature => config[:signature],
8
+ :login => config[:login],
9
+ :password => config[:password]
10
+ end
6
11
 
7
- @ip = Utils.ip
12
+ super(gateway_builder,
13
+ :paypal_express,
14
+ ::Killbill::PaypalExpress::PaypalExpressPaymentMethod,
15
+ ::Killbill::PaypalExpress::PaypalExpressTransaction,
16
+ ::Killbill::PaypalExpress::PaypalExpressResponse)
8
17
 
9
- super
18
+ @ip = ::Killbill::Plugin::ActiveMerchant::Utils.ip
19
+ end
10
20
 
11
- @logger.info "Killbill::PaypalExpress::PaymentPlugin started"
12
- end
21
+ def authorize_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
22
+ # Pass extra parameters for the gateway here
23
+ options = {}
13
24
 
14
- # return DB connections to the Pool if required
15
- def after_request
16
- ActiveRecord::Base.connection.close
17
- end
25
+ add_required_options(kb_payment_transaction_id, kb_payment_method_id, context, options)
18
26
 
19
- def process_payment(kb_account_id, kb_payment_id, kb_payment_method_id, amount, currency, call_context = nil, options = {})
20
- # Use Money to compute the amount in cents, as it depends on the currency (1 cent of BTC is 1 Satoshi, not 0.01 BTC)
21
- amount_in_cents = Money.new_with_amount(amount, currency).cents.to_i
27
+ properties = merge_properties(properties, options)
22
28
 
23
- # If the payment was already made, just return the status
24
- paypal_express_transaction = PaypalExpressTransaction.from_kb_payment_id(kb_payment_id) rescue nil
25
- return paypal_express_transaction.paypal_express_response.to_payment_response unless paypal_express_transaction.nil?
29
+ # Can't use default implementation: the authorize signature is for one-off payments only
30
+ #super(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
31
+ gateway_call_proc = Proc.new do |gateway, linked_transaction, payment_source, amount_in_cents, options|
32
+ gateway.authorize_reference_transaction(amount_in_cents, options)
33
+ end
26
34
 
27
- # Check for currency conversion
28
- actual_amount, actual_currency = convert_amount_currency_if_required(amount_in_cents, currency, kb_payment_id)
35
+ dispatch_to_gateways(:authorize, kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context, gateway_call_proc)
36
+ end
29
37
 
30
- options[:currency] ||= actual_currency.to_s
31
- options[:payment_type] ||= 'Any'
32
- options[:invoice_id] ||= kb_payment_id
33
- options[:description] ||= Killbill::PaypalExpress.paypal_payment_description || "Kill Bill payment for #{kb_payment_id}"
34
- options[:ip] ||= @ip
38
+ def capture_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
39
+ # Pass extra parameters for the gateway here
40
+ options = {
41
+ # NotComplete to allow for partial captures.
42
+ # If Complete, any remaining amount of the original authorized transaction is automatically voided and all remaining open authorizations are voided.
43
+ :complete_type => 'NotComplete'
44
+ }
35
45
 
36
- if options[:reference_id].blank?
37
- payment_method = PaypalExpressPaymentMethod.from_kb_payment_method_id(kb_payment_method_id)
38
- options[:reference_id] = payment_method.paypal_express_baid
46
+ properties = merge_properties(properties, options)
47
+ super(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
39
48
  end
40
49
 
50
+ def purchase_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
51
+ # Pass extra parameters for the gateway here
52
+ options = {}
41
53
 
42
- # Go to Paypal (DoReferenceTransaction call)
43
- paypal_response = @gateway.reference_transaction actual_amount, options
44
- response = save_response_and_transaction paypal_response, :charge, kb_payment_id, actual_amount, actual_currency
45
-
46
- response.to_payment_response
47
- end
54
+ add_required_options(kb_payment_transaction_id, kb_payment_method_id, context, options)
48
55
 
49
- def get_payment_info(kb_account_id, kb_payment_id, tenant_context = nil, options = {})
50
- paypal_express_transaction = PaypalExpressTransaction.from_kb_payment_id(kb_payment_id)
56
+ properties = merge_properties(properties, options)
51
57
 
52
- # We could also re-fetch it via: @gateway.transaction_details(transaction_id)
53
- # but we would need to reconstruct the payment_info object
54
- paypal_express_transaction.paypal_express_response.to_payment_response
55
- end
58
+ # Can't use default implementation: the purchase signature is for one-off payments only
59
+ #super(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
60
+ gateway_call_proc = Proc.new do |gateway, linked_transaction, payment_source, amount_in_cents, options|
61
+ gateway.reference_transaction(amount_in_cents, options)
62
+ end
56
63
 
57
- def process_refund(kb_account_id, kb_payment_id, amount, currency, call_context = nil, options = {})
58
- # Use Money to compute the amount in cents, as it depends on the currency (1 cent of BTC is 1 Satoshi, not 0.01 BTC)
59
- amount_in_cents = Money.new_with_amount(amount, currency).cents.to_i
64
+ dispatch_to_gateways(:purchase, kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context, gateway_call_proc)
65
+ end
60
66
 
61
- # Check for currency conversion
62
- actual_amount, actual_currency = convert_amount_currency_if_required(amount_in_cents, currency, kb_payment_id)
67
+ def void_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, properties, context)
68
+ # Pass extra parameters for the gateway here
69
+ options = {
70
+ # Void the original authorization
71
+ :linked_transaction_type => :authorize
72
+ }
63
73
 
64
- paypal_express_transaction = PaypalExpressTransaction.find_candidate_transaction_for_refund(kb_payment_id, actual_amount)
74
+ properties = merge_properties(properties, options)
75
+ super(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, properties, context)
76
+ end
65
77
 
66
- options[:currency] ||= actual_currency.to_s
67
- options[:refund_type] ||= paypal_express_transaction.amount_in_cents != actual_amount ? 'Partial' : 'Full'
78
+ def credit_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
79
+ # Pass extra parameters for the gateway here
80
+ options = {}
68
81
 
69
- identification = paypal_express_transaction.paypal_express_txn_id
82
+ properties = merge_properties(properties, options)
83
+ super(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
84
+ end
70
85
 
71
- # Go to Paypal
72
- paypal_response = @gateway.refund actual_amount, identification, options
73
- response = save_response_and_transaction paypal_response, :refund, kb_payment_id, actual_amount, actual_currency
86
+ def refund_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
87
+ # Cannot refund based on authorizations (default behavior)
88
+ linked_transaction_type = @transaction_model.purchases_from_kb_payment_id(kb_payment_id, context.tenant_id).size > 0 ? :purchase : :capture
74
89
 
75
- response.to_refund_response
76
- end
90
+ # Pass extra parameters for the gateway here
91
+ options = {
92
+ :linked_transaction_type => linked_transaction_type
93
+ }
77
94
 
78
- def get_refund_info(kb_account_id, kb_payment_id, tenant_context = nil, options = {})
79
- paypal_express_transactions = PaypalExpressTransaction.refunds_from_kb_payment_id(kb_payment_id)
95
+ properties = merge_properties(properties, options)
96
+ super(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
97
+ end
80
98
 
81
- # We could also re-fetch it via: @gateway.transaction_details(transaction_id)
82
- # but we would need to reconstruct the refund_info object
83
- paypal_express_transactions.map { |t| t.paypal_express_response.to_refund_response }
84
- end
99
+ def get_payment_info(kb_account_id, kb_payment_id, properties, context)
100
+ # Pass extra parameters for the gateway here
101
+ options = {}
85
102
 
86
- def add_payment_method(kb_account_id, kb_payment_method_id, payment_method_props, set_default = false, call_context = nil, options = {})
87
- token = (payment_method_props.properties.find { |kv| kv.key == 'token' }).value
88
- return false if token.nil?
89
-
90
- # The payment method should have been created during the setup step (see private api)
91
- payment_method = PaypalExpressPaymentMethod.from_kb_account_id_and_token(kb_account_id, token)
92
-
93
- # Go to Paypal to get the Payer id (GetExpressCheckoutDetails call)
94
- paypal_express_details_response = @gateway.details_for token
95
- response = save_response_and_transaction paypal_express_details_response, :details_for
96
- return false unless response.success?
97
-
98
- payer_id = response.payer_id
99
- unless payer_id.nil?
100
- # Go to Paypal to create the BAID for recurring payments (CreateBillingAgreement call)
101
- paypal_express_baid_response = @gateway.store token
102
- response = save_response_and_transaction paypal_express_baid_response, :create_billing_agreement
103
- return false unless response.success?
104
-
105
- if response.billing_agreement_id.blank?
106
- # If the baid isn't specified (invalid token, maybe expired?), we won't be able to charge that payment method
107
- # See https://github.com/killbill/killbill-paypal-express-plugin/issues/1
108
- logger.warn "No BAID returned by the CreateBillingAgreement call for token #{token} (account #{kb_account_id})"
109
- return false
110
- end
103
+ properties = merge_properties(properties, options)
104
+ super(kb_account_id, kb_payment_id, properties, context)
105
+ end
111
106
 
112
- payment_method.kb_payment_method_id = kb_payment_method_id
113
- payment_method.paypal_express_payer_id = payer_id
114
- payment_method.paypal_express_baid = response.billing_agreement_id
115
- payment_method.save!
107
+ def search_payments(search_key, offset, limit, properties, context)
108
+ # Pass extra parameters for the gateway here
109
+ options = {}
116
110
 
117
- logger.info "Created BAID #{payment_method.paypal_express_baid} for payment method #{kb_payment_method_id} (account #{kb_account_id})"
118
- true
119
- else
120
- logger.warn "Unable to retrieve Payer id details for token #{token} (account #{kb_account_id})"
121
- false
111
+ properties = merge_properties(properties, options)
112
+ super(search_key, offset, limit, properties, context)
122
113
  end
123
- end
124
114
 
125
- def delete_payment_method(kb_account_id, kb_payment_method_id, call_context = nil, options = {})
126
- PaypalExpressPaymentMethod.mark_as_deleted! kb_payment_method_id
127
- end
115
+ def add_payment_method(kb_account_id, kb_payment_method_id, payment_method_props, set_default, properties, context)
116
+ token = find_value_from_properties(properties, 'token')
117
+ return false if token.nil?
118
+
119
+ # Go to Paypal to get the Payer id (GetExpressCheckoutDetails call)
120
+ options = properties_to_hash(properties)
121
+ payment_processor_account_id = options[:payment_processor_account_id] || :default
122
+ gateway = lookup_gateway(payment_processor_account_id)
123
+ gw_response = gateway.details_for(token)
124
+ response, transaction = save_response_and_transaction(gw_response, :details_for, kb_account_id, context.tenant_id, payment_processor_account_id)
125
+ return false unless response.success? and !response.payer_id.blank?
126
+
127
+ # Pass extra parameters for the gateway here
128
+ options = {
129
+ :paypal_express_token => token,
130
+ :paypal_express_payer_id => response.payer_id
131
+ }
132
+
133
+ properties = merge_properties(properties, options)
134
+ super(kb_account_id, kb_payment_method_id, payment_method_props, set_default, properties, context)
135
+ end
128
136
 
129
- def get_payment_method_detail(kb_account_id, kb_payment_method_id, tenant_context = nil, options = {})
130
- PaypalExpressPaymentMethod.from_kb_payment_method_id(kb_payment_method_id).to_payment_method_response
131
- end
137
+ def delete_payment_method(kb_account_id, kb_payment_method_id, properties, context)
138
+ # Pass extra parameters for the gateway here
139
+ options = {}
132
140
 
133
- def set_default_payment_method(kb_account_id, kb_payment_method_id, call_context = nil, options = {})
134
- # No-op
135
- end
141
+ properties = merge_properties(properties, options)
142
+ super(kb_account_id, kb_payment_method_id, properties, context)
143
+ end
136
144
 
137
- def get_payment_methods(kb_account_id, refresh_from_gateway, call_context = nil, options = {})
138
- PaypalExpressPaymentMethod.from_kb_account_id(kb_account_id).collect { |pm| pm.to_payment_method_info_response }
139
- end
145
+ def get_payment_method_detail(kb_account_id, kb_payment_method_id, properties, context)
146
+ # Pass extra parameters for the gateway here
147
+ options = {}
140
148
 
141
- def reset_payment_methods(kb_account_id, payment_methods)
142
- return if payment_methods.nil?
143
-
144
- paypal_pms = PaypalExpressPaymentMethod.from_kb_account_id(kb_account_id)
145
-
146
- payment_methods.delete_if do |payment_method_info_plugin|
147
- should_be_deleted = false
148
- paypal_pms.each do |paypal_pm|
149
- # Do paypal_pm and payment_method_info_plugin represent the same PayPal payment method?
150
- if paypal_pm.external_payment_method_id == payment_method_info_plugin.external_payment_method_id
151
- # Do we already have a kb_payment_method_id?
152
- if paypal_pm.kb_payment_method_id == payment_method_info_plugin.payment_method_id
153
- should_be_deleted = true
154
- break
155
- elsif paypal_pm.kb_payment_method_id.nil?
156
- # We didn't have the kb_payment_method_id - update it
157
- paypal_pm.kb_payment_method_id = payment_method_info_plugin.payment_method_id
158
- should_be_deleted = paypal_pm.save
159
- break
160
- # Otherwise the same BAID points to 2 different kb_payment_method_id. This should never happen,
161
- # but we cowardly will insert a second row below
162
- end
163
- end
164
- end
149
+ properties = merge_properties(properties, options)
150
+ super(kb_account_id, kb_payment_method_id, properties, context)
151
+ end
165
152
 
166
- should_be_deleted
153
+ def set_default_payment_method(kb_account_id, kb_payment_method_id, properties, context)
154
+ # TODO
167
155
  end
168
156
 
169
- # The remaining elements in payment_methods are not in our table (this should never happen?!)
170
- payment_methods.each do |payment_method_info_plugin|
171
- PaypalExpressPaymentMethod.create :kb_account_id => payment_method_info_plugin.account_id,
172
- :kb_payment_method_id => payment_method_info_plugin.payment_method_id,
173
- :paypal_express_baid => payment_method_info_plugin.external_payment_method_id,
174
- :paypal_express_token => 'unknown (created by reset)'
157
+ def get_payment_methods(kb_account_id, refresh_from_gateway, properties, context)
158
+ # Pass extra parameters for the gateway here
159
+ options = {}
160
+
161
+ properties = merge_properties(properties, options)
162
+ super(kb_account_id, refresh_from_gateway, properties, context)
175
163
  end
176
- end
177
164
 
178
- def search_payments(search_key, offset = 0, limit = 100, call_context = nil, options = {})
179
- PaypalExpressResponse.search(search_key, offset, limit, :payment)
180
- end
165
+ def search_payment_methods(search_key, offset, limit, properties, context)
166
+ # Pass extra parameters for the gateway here
167
+ options = {}
181
168
 
182
- def search_refunds(search_key, offset = 0, limit = 100, call_context = nil, options = {})
183
- PaypalExpressResponse.search(search_key, offset, limit, :refund)
184
- end
169
+ properties = merge_properties(properties, options)
170
+ super(search_key, offset, limit, properties, context)
171
+ end
185
172
 
186
- def search_payment_methods(search_key, offset = 0, limit = 100, call_context = nil, options = {})
187
- PaypalExpressPaymentMethod.search(search_key, offset, limit)
188
- end
173
+ def reset_payment_methods(kb_account_id, payment_methods, properties, context)
174
+ super
175
+ end
189
176
 
190
- private
177
+ def build_form_descriptor(kb_account_id, descriptor_fields, properties, context)
178
+ # Pass extra parameters for the gateway here
179
+ options = {}
180
+ properties = merge_properties(properties, options)
191
181
 
192
- def convert_amount_currency_if_required(input_amount, input_currency, kb_payment_id)
182
+ # Add your custom static hidden tags here
183
+ options = {
184
+ #:token => config[:paypal-express][:token]
185
+ }
186
+ descriptor_fields = merge_properties(descriptor_fields, options)
193
187
 
194
- converted_currency = Killbill::PaypalExpress.converted_currency(input_currency)
195
- return [input_amount, input_currency] if converted_currency.nil?
188
+ super(kb_account_id, descriptor_fields, properties, context)
189
+ end
196
190
 
197
- kb_payment = @kb_apis.payment_api.get_payment(kb_payment_id, false, @kb_apis.create_context)
191
+ def process_notification(notification, properties, context)
192
+ # Pass extra parameters for the gateway here
193
+ options = {}
194
+ properties = merge_properties(properties, options)
195
+
196
+ super(notification, properties, context) do |gw_notification, service|
197
+ # Retrieve the payment
198
+ # gw_notification.kb_payment_id =
199
+ #
200
+ # Set the response body
201
+ # gw_notification.entity =
202
+ end
203
+ end
198
204
 
199
- currency_conversion = @kb_apis.currency_conversion_api.get_currency_conversion(converted_currency, kb_payment.effective_date)
200
- rates = currency_conversion.rates
201
- found = rates.select do |r|
202
- r.currency.to_s.upcase.to_sym == input_currency.to_s.upcase.to_sym
205
+ def to_express_checkout_url(response, options = {})
206
+ payment_processor_account_id = options[:payment_processor_account_id] || :default
207
+ gateway = lookup_gateway(payment_processor_account_id)
208
+ gateway.redirect_url_for(response.token)
203
209
  end
204
210
 
205
- if found.nil? || found.empty?
206
- @logger.warn "Failed to find converted currency #{converted_currency} for input currency #{input_currency}"
207
- return [input_amount, input_currency]
211
+ protected
212
+
213
+ def get_active_merchant_module
214
+ ::OffsitePayments.integration(:paypal)
208
215
  end
209
216
 
210
- # conversion rounding ?
211
- conversion_rate = found[0].value
212
- output_amount = input_amount * conversion_rate
213
- return [output_amount.to_i, converted_currency]
214
- end
217
+ private
215
218
 
216
- def save_response_and_transaction(paypal_express_response, api_call, kb_payment_id=nil, amount_in_cents=0, currency=nil)
217
- @logger.warn "Unsuccessful #{api_call}: #{paypal_express_response.message}" unless paypal_express_response.success?
219
+ def add_required_options(kb_payment_transaction_id, kb_payment_method_id, context, options)
220
+ payment_method = @payment_method_model.from_kb_payment_method_id(kb_payment_method_id, context.tenant_id)
218
221
 
219
- # Save the response to our logs
220
- response = PaypalExpressResponse.from_response(api_call, kb_payment_id, paypal_express_response)
221
- response.save!
222
+ options[:payer_id] ||= payment_method.paypal_express_payer_id
223
+ options[:token] ||= payment_method.paypal_express_token
224
+ options[:reference_id] ||= payment_method.token # baid
222
225
 
223
- if response.success and !kb_payment_id.blank? and !response.authorization.blank?
224
- # Record the transaction
225
- transaction = response.create_paypal_express_transaction!(:amount_in_cents => amount_in_cents,
226
- :currency => currency,
227
- :api_call => api_call,
228
- :kb_payment_id => kb_payment_id,
229
- :paypal_express_txn_id => response.authorization)
230
- @logger.debug "Recorded transaction: #{transaction.inspect}"
226
+ options[:payment_type] ||= 'Any'
227
+ options[:invoice_id] ||= kb_payment_transaction_id
228
+ options[:ip] ||= @ip
231
229
  end
232
- response
233
230
  end
234
231
  end
235
232
  end