pay-lago 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/Gemfile.lock +2 -2
  4. data/README.md +2 -5
  5. data/app/models/concerns/pay/lago/pay_payment_method_extensions.rb +23 -0
  6. data/docs/4_payment_methods.md +40 -0
  7. data/lib/pay/lago/billable.rb +53 -12
  8. data/lib/pay/lago/engine.rb +2 -1
  9. data/lib/pay/lago/payment_method.rb +44 -6
  10. data/lib/pay/lago/subscription.rb +6 -13
  11. data/lib/pay/lago/version.rb +1 -1
  12. data/lib/pay/lago/webhooks/customer_payment_provider_created.rb +1 -7
  13. data/pay-lago.gemspec +1 -1
  14. data/test/dummy/db/migrate/1_create_pay_tables.rb +93 -0
  15. data/test/dummy/db/schema.rb +17 -16
  16. data/test/fixtures/pay/customers.yml +1 -0
  17. data/test/pay/lago/billable_test.rb +1 -1
  18. data/test/pay/lago/payment_method_test.rb +34 -0
  19. data/test/pay/lago/subscription_test.rb +3 -5
  20. data/test/vcr_cassettes/test_PaymentMethod_can_be_made_default__updating_Lago.yml +163 -0
  21. data/test/vcr_cassettes/test_add_payment_method_to_lago.yml +62 -8
  22. data/test/vcr_cassettes/test_can_get_lago_processor_customer.yml +7 -7
  23. data/test/vcr_cassettes/test_can_make_a_lago_processor_charge.yml +22 -22
  24. data/test/vcr_cassettes/test_generates_lago_processor_id.yml +8 -59
  25. data/test/vcr_cassettes/test_lago_cannot_change_quantity.yml +16 -16
  26. data/test/vcr_cassettes/test_lago_processor_cancel.yml +32 -32
  27. data/test/vcr_cassettes/test_lago_processor_charge.yml +55 -55
  28. data/test/vcr_cassettes/test_lago_processor_on_grace_period__is_always_false.yml +16 -16
  29. data/test/vcr_cassettes/test_lago_processor_pause_raises_an_error.yml +16 -16
  30. data/test/vcr_cassettes/test_lago_processor_paused__is_always_false.yml +16 -16
  31. data/test/vcr_cassettes/test_lago_processor_refund_with_premium.yml +64 -64
  32. data/test/vcr_cassettes/test_lago_processor_resume_raises_an_error.yml +16 -16
  33. data/test/vcr_cassettes/test_lago_processor_subscribe.yml +17 -16
  34. data/test/vcr_cassettes/test_lago_processor_subscription.yml +24 -24
  35. data/test/vcr_cassettes/test_lago_processor_swap.yml +86 -32
  36. metadata +10 -5
  37. /data/docs/{4_webhooks.md → 5_webhooks.md} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a9c2b824c5412e64bb1f0c2efed6c5532265b9ff4978c8368d98d3fc7cba2cc2
4
- data.tar.gz: fee44626ac0dcc1082cc688f74d56c74146fa33b3ee001c35ce1b3a3f729991b
3
+ metadata.gz: '09ba7ef76f7ab46e24c8a7602c22eaba9705551eb7406304ff9b3533fcda48a4'
4
+ data.tar.gz: a5c1b1cfee26e0afd36431a5e2480807ee03a378bf01829facbf0bcf9dc271de
5
5
  SHA512:
6
- metadata.gz: 76cefb859ce9d0fed7736cf6a699517efbe36125d15be22334508fdd3daba204b9e283eae982889f2e68c0f35e88b258e3a5fdef856733f18ff6faff748d697c
7
- data.tar.gz: 507d1b89c54951857c29f8d728d20ad902bd907943243a6349189197a1a56915a1309434c4efc24e3ee997e0905f0e245420341bfe38f46bd835c41a24073271
6
+ metadata.gz: 427777503e12fbddfc7b63ec98d945f98695dfb7f3aee42cfed8a3acd9002cf892c2fc0cce52c46b63cfdfda596da479a7862eb706e05b680a0fa81d97d6be80
7
+ data.tar.gz: c579510c4be89f0e92b30ce057722561c5fed149c9808f77a1518f867dbebde53560de1662885f85f1e7d401313658914653eaf018382b7dae55b40e42cd89b6
data/CHANGELOG.md CHANGED
@@ -1,2 +1,14 @@
1
+ ## [0.1.1] - 2023-12-11
2
+ ### Added
3
+ - Scope for Lago payment methods with a given provider.
4
+ - Method to find a Lago payment method by provider, and provider id.
5
+ - Added `pull!` and `push!` methods to billable, and payment method, to better control syncing with Lago.
6
+ - Method to retrieve a payment method on billable.
7
+
8
+ ### Changed
9
+ - Dependency `lago-ruby-client` was upgraded to version 0.52.2.pre.beta.
10
+ - Improved API for adding a payment method to billable.
11
+ - Method `subscription` on subscription can be passed `reload: true` to reload the subscription data.
12
+
1
13
  ## [0.1.0] - 2023-10-23
2
14
  - Initial release
data/Gemfile.lock CHANGED
@@ -11,7 +11,7 @@ PATH
11
11
  remote: .
12
12
  specs:
13
13
  pay-lago (0.1.0)
14
- lago-ruby-client (= 0.50.0.pre.beta)
14
+ lago-ruby-client (= 0.53.0.pre.beta)
15
15
  pay (~> 6.8)
16
16
 
17
17
  GEM
@@ -119,7 +119,7 @@ GEM
119
119
  reline (>= 0.3.8)
120
120
  json (2.6.3)
121
121
  jwt (2.7.1)
122
- lago-ruby-client (0.50.0.pre.beta)
122
+ lago-ruby-client (0.53.0.pre.beta)
123
123
  jwt
124
124
  openssl
125
125
  language_server-protocol (3.17.0.3)
data/README.md CHANGED
@@ -63,9 +63,6 @@ for direct use of the [Lago API](https://docs.getlago.com/api-reference/intro).
63
63
 
64
64
  The client instance can be accessed from `Pay::Lago.client`.
65
65
 
66
- #### Please Note:
67
- For any API calls that would use a GET, the external_id must be [URI encoded](https://ruby-doc.org/current/stdlibs/uri/URI.html#method-c-encode_www_form_component), as the lago-ruby-client gem does not currently do this itself. See [this issue](https://github.com/getlago/lago-ruby-client/issues/136).
68
-
69
66
  ## Development
70
67
 
71
68
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -74,7 +71,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
74
71
 
75
72
  ## Contributing
76
73
 
77
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/pay-lago. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/pay-lago/blob/master/CODE_OF_CONDUCT.md).
74
+ Bug reports and pull requests are welcome on GitHub at https://github.com/demingfactor/pay-lago. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/demingfactor/pay-lago/blob/master/CODE_OF_CONDUCT.md).
78
75
 
79
76
  ## License
80
77
 
@@ -82,4 +79,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
82
79
 
83
80
  ## Code of Conduct
84
81
 
85
- Everyone interacting in the Pay::Lago project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/pay-lago/blob/master/CODE_OF_CONDUCT.md).
82
+ Everyone interacting in the Pay::Lago project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/demingfactor/pay-lago/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,23 @@
1
+ module Pay
2
+ module Lago
3
+ module PayPaymentMethodExtensions
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ scope :with_provider, ->(provider) { where("pay_payment_methods.data->>'payment_provider' = ?", provider) }
8
+ end
9
+
10
+ def provider
11
+ data["payment_provider"].to_sym
12
+ rescue
13
+ nil
14
+ end
15
+
16
+ class_methods do
17
+ def find_by_lago_provider_and_id(provider, provider_id)
18
+ with_provider(provider).find_by_processor_and_id(:lago, provider_id)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,40 @@
1
+ # Payment Methods
2
+
3
+ ## Adding a Payment Method to a Customer
4
+
5
+ Lago uses the default method of payment for a given customer. As such, Payment Methods in this gem actually
6
+ store customer information of the given provider, rather than the payment method itself.
7
+
8
+ ```ruby
9
+ # Adding a Stripe customer as a Payment Method
10
+ customer = Pay::Customer.find(1234)
11
+ customer.add_payment_method(:stripe, "cus_1234")
12
+ ```
13
+
14
+ ```ruby
15
+ # Adding a Stripe customer as a Payment Method, but make it not the default.
16
+ customer = Pay::Customer.find(1234)
17
+ customer.add_payment_method(:stripe, "cus_1234", default: false)
18
+ ```
19
+
20
+ ```ruby
21
+ # Adding a Stripe customer as a Payment Method, but make it not sync with Lago.
22
+ # NOTE: Syncing with Lago will only work anyways if default is true (which it is by default).
23
+ customer = Pay::Customer.find(1234)
24
+ customer.add_payment_method(:stripe, "cus_1234", sync: false)
25
+ ```
26
+
27
+ ## Retrieving a Payment Method
28
+
29
+ ```ruby
30
+ # Retrieve a Payment Method with a given ID.
31
+ customer = Pay::Customer.find(1234)
32
+ customer.get_payment_method(:stripe, "cus_1234")
33
+ ```
34
+
35
+ ```ruby
36
+ # If no Payment Method ID is given, it will return either the first Payment Method
37
+ # for the customer with the given provider, or the default Payment Method (if provider matches).
38
+ customer = Pay::Customer.find(1234)
39
+ customer.get_payment_method(:stripe)
40
+ ```
@@ -39,21 +39,36 @@ module Pay
39
39
 
40
40
  def customer
41
41
  begin
42
- lago_customer = Lago.client.customers.get(uri_escape(pay_external_id))
42
+ lago_customer = Lago.client.customers.get(pay_external_id)
43
43
  rescue ::Lago::Api::HttpError
44
44
  lago_customer = Lago.client.customers.create(customer_attributes(pay_external_id))
45
45
  end
46
- pay_customer.update!(processor_id: lago_customer.external_id) unless processor_id == lago_customer.external_id
46
+ pull!(lago_customer)
47
47
  lago_customer
48
48
  rescue ::Lago::Api::HttpError => e
49
49
  raise Pay::Lago::Error, e
50
50
  end
51
51
 
52
- def update_customer!(**attributes)
52
+ def update_customer!(attributes = {})
53
+ attributes = symbolize_recursive(attributes)
53
54
  new_attributes = Lago.openstruct_to_h(customer).except(:lago_id)
54
- Lago.client.customers.create(
55
+ lago_customer = Lago.client.customers.create(
55
56
  new_attributes.merge(attributes.except(:external_id))
56
57
  )
58
+ pull!(lago_customer)
59
+ lago_customer
60
+ end
61
+
62
+ def pull!(lago_customer = customer, force: false)
63
+ if pay_customer.processor_id.present? && pay_customer.processor_id != lago_customer.external_id && !force
64
+ raise Pay::Lago::Error.new("Error syncing Lago Customer: IDs do not match!")
65
+ end
66
+ new_data = Pay::Lago.openstruct_to_h(lago_customer)
67
+ pay_customer.update!(processor_id: lago_customer.external_id, data: new_data)
68
+ end
69
+
70
+ def push!
71
+ update_customer!(pay_customer.data)
57
72
  end
58
73
 
59
74
  def charge(amount = nil, addon: nil, options: {})
@@ -91,7 +106,7 @@ module Pay
91
106
 
92
107
  begin
93
108
  subscription = Lago.client.subscriptions.create(attributes)
94
- rescue ::Lago::Api::HttpError
109
+ rescue ::Lago::Api::HttpError => e
95
110
  pay_subscription.destroy!
96
111
  raise Pay::Lago::Error, e
97
112
  end
@@ -100,12 +115,38 @@ module Pay
100
115
  Pay::Lago::Subscription.sync(lago_customer.external_id, external_id, object: subscription)
101
116
  end
102
117
 
103
- def add_payment_method(_token = nil, default: true)
104
- Pay::Lago::PaymentMethod.sync(pay_customer: pay_customer)
118
+ def get_payment_method(provider, provider_id = nil)
119
+ raise Pay::Lago::Error.new("Invalid provider!") unless Pay::Lago::PaymentMethod.valid_provider?(provider)
120
+ return Pay::PaymentMethod.find_by_lago_provider_and_id(provider, provider_id) if provider_id.present?
121
+ return pay_customer.default_payment_method if pay_customer.default_payment_method&.provider == provider
122
+ Pay::PaymentMethod.with_provider(provider).first
123
+ end
124
+
125
+ def add_payment_method(provider, provider_id, default: true, sync: true, options: {})
126
+ raise Pay::Lago::Error.new("Invalid provider!") unless Pay::Lago::PaymentMethod.valid_provider?(provider)
127
+
128
+ payment_method = pay_customer.payment_methods.with_provider(provider).where(processor_id: provider_id).first
129
+ payment_method ||= Pay::PaymentMethod.create(
130
+ processor_id: provider_id,
131
+ type: "card",
132
+ customer: pay_customer, data: options.merge(payment_provider: provider, provider_customer_id: provider_id)
133
+ )
134
+
135
+ payment_method.make_default! if default
136
+ payment_method.payment_processor.push! if sync && default
137
+
138
+ payment_method
139
+ end
140
+
141
+ def clear_payment_method!
142
+ data = (pay_customer.data || {}).merge(
143
+ "billing_configuration": {payment_provider: nil, payment_method_id: nil, provider_customer_id: nil, provider_payment_methods: nil}
144
+ )
145
+ update_customer!(data)
105
146
  end
106
147
 
107
- def processor_subscription(subscription_id, options = {})
108
- Pay::Lago::Subscription.get_subscription(processor_id, subscription_id)
148
+ def processor_subscription(subscription_id, **_options)
149
+ Pay::Lago.client.subscriptions.get(subscription_id)
109
150
  end
110
151
 
111
152
  def trial_end_date(subscription)
@@ -139,9 +180,9 @@ module Pay
139
180
  raise Pay::Lago::Error, e
140
181
  end
141
182
 
142
- def uri_escape(uri)
143
- return "" unless uri.is_a? String
144
- URI.encode_www_form_component uri
183
+ def symbolize_recursive(hash)
184
+ return hash unless hash.is_a? Hash
185
+ hash.map { |key, value| [key.is_a?(String) ? key.to_sym : key, symbolize_recursive(value)] }.to_h
145
186
  end
146
187
  end
147
188
  end
@@ -3,7 +3,7 @@ module Pay
3
3
  class Engine < ::Rails::Engine
4
4
  engine_name "pay_lago"
5
5
 
6
- initializer "pay_lago.processors" do |app|
6
+ initializer "pay_lago.processors", after: "pay.processors" do |app|
7
7
  ActiveSupport.on_load(:active_record) do
8
8
  include Pay::Lago::Attributes
9
9
  end
@@ -18,6 +18,7 @@ module Pay
18
18
  Pay::Customer.include Pay::Lago::PayCustomerExtensions
19
19
  Pay::Charge.include Pay::Lago::PayExtensions
20
20
  Pay::Subscription.include Pay::Lago::PayExtensions
21
+ Pay::PaymentMethod.include Pay::Lago::PayPaymentMethodExtensions
21
22
 
22
23
  # Prepend Pay::Lago extensions
23
24
  Pay::Webhook.prepend Pay::Lago::WebhookExtensions
@@ -1,23 +1,40 @@
1
1
  module Pay
2
2
  module Lago
3
3
  class PaymentMethod
4
+ VALID_PROVIDERS = %i[stripe adyen gocardless]
4
5
  attr_reader :pay_payment_method
5
6
 
6
7
  delegate :customer, :processor_id, to: :pay_payment_method
7
8
 
8
- # Lago doesn't provide PaymentMethod IDs, so we have to lookup via the Customer
9
- def self.sync(pay_customer:)
10
- payment_method = pay_customer.default_payment_method || pay_customer.build_default_payment_method
11
- lago_customer = pay_customer.customer
9
+ def self.valid_provider?(provider)
10
+ VALID_PROVIDERS.include?(provider.to_s.to_sym)
11
+ end
12
+
13
+ # Lago doesn't provide PaymentMethod IDs, so we have to lookup via the Customer.
14
+ # The most recently synced payment method is *always* the default.
15
+ def self.sync(lago_customer)
16
+ pay_customer = Pay::Customer.find_by(processor_id: lago_customer.external_id)
17
+ if pay_customer.blank?
18
+ Rails.logger.debug "Pay::Customer #{lago_customer.external_id} is not in the database while syncing Payment Method
19
+ #{lago_customer.provider_customer_id}"
20
+ return
21
+ end
22
+
12
23
  payment_data = lago_customer.billing_configuration
24
+ provider_id = payment_data.provider_customer_id
25
+
26
+ payment_method = pay_customer.payment_methods.find_by(processor_id: provider_id)
27
+ payment_method ||= pay_customer.build_default_payment_method
13
28
 
14
29
  payment_method_attr = {
15
- processor_id: payment_data.provider_customer_id || NanoId.generate,
30
+ processor_id: provider_id,
16
31
  type: "card",
17
32
  data: Lago.openstruct_to_h(payment_data)
18
33
  }
19
34
 
20
35
  payment_method.update!(payment_method_attr)
36
+ payment_method.make_default!
37
+
21
38
  payment_method
22
39
  rescue ::Lago::Api::HttpError => e
23
40
  raise Pay::Lago::Error, e
@@ -27,10 +44,31 @@ module Pay
27
44
  @pay_payment_method = pay_payment_method
28
45
  end
29
46
 
47
+ def pull!
48
+ return false unless pay_payment_method.default?
49
+ customer.pull!
50
+ pay_payment_method.update!(data: customer.data["billing_configuration"])
51
+ end
52
+
53
+ def push!
54
+ return false unless pay_payment_method.default?
55
+ update_customer!
56
+ customer.push!
57
+ end
58
+
30
59
  def make_default!
60
+ update_customer!
31
61
  end
32
62
 
33
- def detach
63
+ def detach!
64
+ customer.clear_payment_method!
65
+ end
66
+
67
+ private
68
+
69
+ def update_customer!
70
+ data = (customer.data || {}).merge("billing_configuration": pay_payment_method.data)
71
+ customer.update!(data: data)
34
72
  end
35
73
  end
36
74
  end
@@ -23,7 +23,7 @@ module Pay
23
23
  to: :pay_subscription
24
24
 
25
25
  def self.sync(customer_id, subscription_id, object: nil, try: 0, retries: 1)
26
- object ||= get_subscription(customer_id, subscription_id)
26
+ object ||= Pay::Lago.client.subscriptions.get(subscription_id)
27
27
 
28
28
  pay_customer = Pay::Customer.find_by(processor: :lago, processor_id: customer_id)
29
29
  if pay_customer.blank?
@@ -60,25 +60,18 @@ module Pay
60
60
  end
61
61
  end
62
62
 
63
- def self.get_subscription(customer_id, external_id)
64
- result = Lago.client.subscriptions.get_all(external_customer_id: customer_id, per_page: 9223372036854775807)
65
- result.subscriptions.each { |sub| return sub if sub.external_id == external_id }
66
- raise Pay::Lago::Error.new("No subscription could be found.")
67
- rescue ::Lago::Api::HttpError => e
68
- raise Pay::Lago::Error, e
69
- end
70
-
71
63
  def initialize(pay_subscription)
72
64
  @pay_subscription = pay_subscription
73
65
  end
74
66
 
75
- def subscription
76
- @lago_subscription ||= self.class.get_subscription(pay_subscription.customer.processor_id, processor_id)
67
+ def subscription(reload: false)
68
+ @lago_subscription = nil if reload
69
+ @lago_subscription ||= Pay::Lago.client.subscriptions.get(processor_id)
77
70
  end
78
71
 
79
72
  def cancel(**options)
80
73
  customer_id = subscription.external_customer_id
81
- response = Lago.client.subscriptions.destroy(URI.encode_www_form_component(processor_id), options: options)
74
+ response = Lago.client.subscriptions.destroy(processor_id, options: options)
82
75
  self.class.sync(customer_id, processor_id, object: response)
83
76
  rescue ::Lago::Api::HttpError => e
84
77
  raise Pay::Lago::Error, e
@@ -117,7 +110,7 @@ module Pay
117
110
  customer_id = subscription.external_customer_id
118
111
  attributes = options.merge(
119
112
  external_customer_id: customer_id,
120
- external_id: processor_id, plan_code: plan
113
+ external_id: processor_id, plan_code: plan.to_s
121
114
  )
122
115
  new_subscription = Lago.client.subscriptions.create(attributes)
123
116
  self.class.sync(customer_id, processor_id, object: new_subscription)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Pay
4
4
  module Lago
5
- VERSION = "0.1.0"
5
+ VERSION = "0.1.1"
6
6
  end
7
7
  end
@@ -4,13 +4,7 @@ module Pay
4
4
  class CustomerPaymentProviderCreated
5
5
  def call(event)
6
6
  customer = event.customer
7
- pay_customer = Pay::Customer.find_by(processor_id: customer.external_id)
8
- unless pay_customer.present?
9
- Rails.logger.debug "Pay::Customer #{customer.external_id} is not in the database while syncing Payment Information"
10
- return false
11
- end
12
-
13
- pay_customer.update!(data: Pay::Lago.openstruct_to_h(customer))
7
+ PaymentMethod.sync(customer)
14
8
  end
15
9
  end
16
10
  end
data/pay-lago.gemspec CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  # Uncomment to register a new dependency of your gem
22
22
  spec.add_dependency "pay", "~> 6.8"
23
- spec.add_dependency "lago-ruby-client", "0.50.0-beta"
23
+ spec.add_dependency "lago-ruby-client", "0.53.0-beta"
24
24
 
25
25
  # For more information and examples about making a new gem, check out our
26
26
  # guide at: https://bundler.io/guides/creating_gem.html
@@ -0,0 +1,93 @@
1
+ class CreatePayTables < ActiveRecord::Migration[6.0]
2
+ def change
3
+ primary_key_type, foreign_key_type = primary_and_foreign_key_types
4
+
5
+ create_table :pay_customers, id: primary_key_type do |t|
6
+ t.belongs_to :owner, polymorphic: true, index: false, type: foreign_key_type
7
+ t.string :processor, null: false
8
+ t.string :processor_id
9
+ t.boolean :default
10
+ t.public_send Pay::Adapter.json_column_type, :data
11
+ t.datetime :deleted_at
12
+ t.string :stripe_account
13
+ t.timestamps
14
+ end
15
+ add_index :pay_customers, [:owner_type, :owner_id, :deleted_at], name: :pay_customer_owner_index, unique: true
16
+ add_index :pay_customers, [:processor, :processor_id], unique: true
17
+
18
+ create_table :pay_merchants, id: primary_key_type do |t|
19
+ t.belongs_to :owner, polymorphic: true, index: false, type: foreign_key_type
20
+ t.string :processor, null: false
21
+ t.string :processor_id
22
+ t.boolean :default
23
+ t.public_send Pay::Adapter.json_column_type, :data
24
+ t.timestamps
25
+ end
26
+ add_index :pay_merchants, [:owner_type, :owner_id, :processor]
27
+
28
+ create_table :pay_payment_methods, id: primary_key_type do |t|
29
+ t.belongs_to :customer, foreign_key: {to_table: :pay_customers}, null: false, index: false, type: foreign_key_type
30
+ t.string :processor_id, null: false
31
+ t.boolean :default
32
+ t.string :type
33
+ t.public_send Pay::Adapter.json_column_type, :data
34
+ t.timestamps
35
+ end
36
+ add_index :pay_payment_methods, [:customer_id, :processor_id], unique: true
37
+
38
+ create_table :pay_subscriptions, id: primary_key_type do |t|
39
+ t.belongs_to :customer, foreign_key: {to_table: :pay_customers}, null: false, index: false, type: foreign_key_type
40
+ t.string :name, null: false
41
+ t.string :processor_id, null: false
42
+ t.string :processor_plan, null: false
43
+ t.integer :quantity, default: 1, null: false
44
+ t.string :status, null: false
45
+ t.datetime :current_period_start
46
+ t.datetime :current_period_end
47
+ t.datetime :trial_ends_at
48
+ t.datetime :ends_at
49
+ t.boolean :metered
50
+ t.string :pause_behavior
51
+ t.datetime :pause_starts_at
52
+ t.datetime :pause_resumes_at
53
+ t.decimal :application_fee_percent, precision: 8, scale: 2
54
+ t.public_send Pay::Adapter.json_column_type, :metadata
55
+ t.public_send Pay::Adapter.json_column_type, :data
56
+ t.timestamps
57
+ end
58
+ add_index :pay_subscriptions, [:customer_id, :processor_id], unique: true
59
+ add_index :pay_subscriptions, [:metered]
60
+ add_index :pay_subscriptions, [:pause_starts_at]
61
+
62
+ create_table :pay_charges, id: primary_key_type do |t|
63
+ t.belongs_to :customer, foreign_key: {to_table: :pay_customers}, null: false, index: false, type: foreign_key_type
64
+ t.belongs_to :subscription, foreign_key: {to_table: :pay_subscriptions}, null: true, type: foreign_key_type
65
+ t.string :processor_id, null: false
66
+ t.integer :amount, null: false
67
+ t.string :currency
68
+ t.integer :application_fee_amount
69
+ t.integer :amount_refunded
70
+ t.public_send Pay::Adapter.json_column_type, :metadata
71
+ t.public_send Pay::Adapter.json_column_type, :data
72
+ t.timestamps
73
+ end
74
+ add_index :pay_charges, [:customer_id, :processor_id], unique: true
75
+
76
+ create_table :pay_webhooks, id: primary_key_type do |t|
77
+ t.string :processor
78
+ t.string :event_type
79
+ t.public_send Pay::Adapter.json_column_type, :event
80
+ t.timestamps
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ def primary_and_foreign_key_types
87
+ config = Rails.configuration.generators
88
+ setting = config.options[config.orm][:primary_key_type]
89
+ primary_key_type = setting || :primary_key
90
+ foreign_key_type = setting || :bigint
91
+ [primary_key_type, foreign_key_type]
92
+ end
93
+ end
@@ -10,7 +10,7 @@
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- ActiveRecord::Schema[7.1].define(version: 2022_08_31_153001) do
13
+ ActiveRecord::Schema[7.1].define(version: 2017_02_05_000000) do
14
14
  create_table "accounts", force: :cascade do |t|
15
15
  t.string "email"
16
16
  t.string "merchant_processor"
@@ -18,8 +18,8 @@ ActiveRecord::Schema[7.1].define(version: 2022_08_31_153001) do
18
18
  end
19
19
 
20
20
  create_table "pay_charges", force: :cascade do |t|
21
- t.integer "customer_id", null: false
22
- t.integer "subscription_id"
21
+ t.bigint "customer_id", null: false
22
+ t.bigint "subscription_id"
23
23
  t.string "processor_id", null: false
24
24
  t.integer "amount", null: false
25
25
  t.string "currency"
@@ -40,10 +40,11 @@ ActiveRecord::Schema[7.1].define(version: 2022_08_31_153001) do
40
40
  t.string "processor_id"
41
41
  t.boolean "default"
42
42
  t.json "data"
43
- t.datetime "deleted_at"
43
+ t.datetime "deleted_at", precision: nil
44
+ t.string "stripe_account"
44
45
  t.datetime "created_at", null: false
45
46
  t.datetime "updated_at", null: false
46
- t.index ["owner_type", "owner_id", "deleted_at", "default"], name: "pay_customer_owner_index"
47
+ t.index ["owner_type", "owner_id", "deleted_at"], name: "pay_customer_owner_index", unique: true
47
48
  t.index ["processor", "processor_id"], name: "index_pay_customers_on_processor_and_processor_id", unique: true
48
49
  end
49
50
 
@@ -60,7 +61,7 @@ ActiveRecord::Schema[7.1].define(version: 2022_08_31_153001) do
60
61
  end
61
62
 
62
63
  create_table "pay_payment_methods", force: :cascade do |t|
63
- t.integer "customer_id", null: false
64
+ t.bigint "customer_id", null: false
64
65
  t.string "processor_id", null: false
65
66
  t.boolean "default"
66
67
  t.string "type"
@@ -71,25 +72,25 @@ ActiveRecord::Schema[7.1].define(version: 2022_08_31_153001) do
71
72
  end
72
73
 
73
74
  create_table "pay_subscriptions", force: :cascade do |t|
74
- t.integer "customer_id", null: false
75
+ t.bigint "customer_id", null: false
75
76
  t.string "name", null: false
76
77
  t.string "processor_id", null: false
77
78
  t.string "processor_plan", null: false
78
79
  t.integer "quantity", default: 1, null: false
79
80
  t.string "status", null: false
80
- t.datetime "current_period_start"
81
- t.datetime "current_period_end"
82
- t.datetime "trial_ends_at"
83
- t.datetime "ends_at"
81
+ t.datetime "current_period_start", precision: nil
82
+ t.datetime "current_period_end", precision: nil
83
+ t.datetime "trial_ends_at", precision: nil
84
+ t.datetime "ends_at", precision: nil
85
+ t.boolean "metered"
86
+ t.string "pause_behavior"
87
+ t.datetime "pause_starts_at", precision: nil
88
+ t.datetime "pause_resumes_at", precision: nil
84
89
  t.decimal "application_fee_percent", precision: 8, scale: 2
85
90
  t.json "metadata"
86
91
  t.json "data"
87
92
  t.datetime "created_at", null: false
88
93
  t.datetime "updated_at", null: false
89
- t.boolean "metered"
90
- t.string "pause_behavior"
91
- t.datetime "pause_starts_at"
92
- t.datetime "pause_resumes_at"
93
94
  t.index ["customer_id", "processor_id"], name: "index_pay_subscriptions_on_customer_id_and_processor_id", unique: true
94
95
  t.index ["metered"], name: "index_pay_subscriptions_on_metered"
95
96
  t.index ["pause_starts_at"], name: "index_pay_subscriptions_on_pause_starts_at"
@@ -107,7 +108,7 @@ ActiveRecord::Schema[7.1].define(version: 2022_08_31_153001) do
107
108
  t.string "email"
108
109
  t.string "name"
109
110
  t.string "owner_type"
110
- t.bigint "owner_id"
111
+ t.integer "owner_id"
111
112
  t.index ["owner_type", "owner_id"], name: "index_teams_on_owner_type_and_owner_id"
112
113
  end
113
114
 
@@ -5,3 +5,4 @@ lago:
5
5
  processor: lago
6
6
  processor_id: gid://dummy/Pay::Customer/772847388
7
7
  default: true
8
+ data: {}
@@ -33,7 +33,7 @@ class Pay::Lago::Billable::Test < ActiveSupport::TestCase
33
33
 
34
34
  test "add payment method to lago" do
35
35
  assert_difference "Pay::PaymentMethod.count" do
36
- @pay_customer.add_payment_method("x", default: true)
36
+ @pay_customer.add_payment_method(:stripe, "x", default: true)
37
37
  end
38
38
 
39
39
  payment_method = @pay_customer.default_payment_method
@@ -0,0 +1,34 @@
1
+ require "test_helper"
2
+ require "uri"
3
+
4
+ class Pay::Lago::PaymentMethodTest < ActiveSupport::TestCase
5
+ setup do
6
+ @pay_customer = pay_customers(:lago)
7
+ end
8
+
9
+ test "Lago sync returns Pay::PaymentMethod" do
10
+ lago_customer = OpenStruct.new(
11
+ external_id: @pay_customer.processor_id,
12
+ billing_configuration: OpenStruct.new(
13
+ payment_provider: "stripe",
14
+ provider_customer_id: "x"
15
+ )
16
+ )
17
+ pay_payment_method = Pay::Lago::PaymentMethod.sync(lago_customer)
18
+ assert pay_payment_method.is_a?(Pay::PaymentMethod)
19
+ assert pay_payment_method.processor_id == "x"
20
+ assert @pay_customer.default_payment_method == pay_payment_method
21
+ end
22
+
23
+ test "PaymentMethod does not push or pull if not default" do
24
+ pay_payment_method = @pay_customer.add_payment_method(:stripe, "test_1234", default: false, sync: false).payment_processor
25
+ assert pay_payment_method.push! == false
26
+ assert pay_payment_method.pull! == false
27
+ end
28
+
29
+ test "PaymentMethod can be made default, updating Lago" do
30
+ pay_payment_method = @pay_customer.add_payment_method(:stripe, "test_1234_a", sync: true)
31
+ lago_customer = Pay::Lago.client.customers.get(@pay_customer.pay_external_id)
32
+ assert lago_customer.billing_configuration.provider_customer_id == pay_payment_method.processor_id
33
+ end
34
+ end