pay-lago 0.1.0 → 0.1.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 (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