pay-abacatepay 0.1.0.pre.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +10 -0
- data/MIT-LICENSE +21 -0
- data/README.md +248 -0
- data/app/controllers/pay/webhooks/abacatepay_controller.rb +104 -0
- data/lib/generators/pay_abacatepay/install/install_generator.rb +21 -0
- data/lib/generators/pay_abacatepay/install/templates/create_pay_abacatepay_processed_webhooks.rb.tt +15 -0
- data/lib/pay/abacatepay/charge.rb +77 -0
- data/lib/pay/abacatepay/customer.rb +136 -0
- data/lib/pay/abacatepay/engine.rb +33 -0
- data/lib/pay/abacatepay/payment_method.rb +21 -0
- data/lib/pay/abacatepay/processed_webhook.rb +19 -0
- data/lib/pay/abacatepay/subscription.rb +88 -0
- data/lib/pay/abacatepay/version.rb +5 -0
- data/lib/pay/abacatepay/webhooks/checkout_completed.rb +52 -0
- data/lib/pay/abacatepay/webhooks/checkout_disputed.rb +11 -0
- data/lib/pay/abacatepay/webhooks/checkout_lost.rb +11 -0
- data/lib/pay/abacatepay/webhooks/checkout_refunded.rb +38 -0
- data/lib/pay/abacatepay/webhooks/event.rb +95 -0
- data/lib/pay/abacatepay/webhooks/payout_completed.rb +11 -0
- data/lib/pay/abacatepay/webhooks/payout_failed.rb +11 -0
- data/lib/pay/abacatepay/webhooks/subscription_cancelled.rb +34 -0
- data/lib/pay/abacatepay/webhooks/subscription_completed.rb +53 -0
- data/lib/pay/abacatepay/webhooks/subscription_renewed.rb +45 -0
- data/lib/pay/abacatepay/webhooks/subscription_trial_started.rb +13 -0
- data/lib/pay/abacatepay/webhooks/transfer_completed.rb +11 -0
- data/lib/pay/abacatepay/webhooks/transfer_failed.rb +11 -0
- data/lib/pay/abacatepay/webhooks/transparent_completed.rb +11 -0
- data/lib/pay/abacatepay/webhooks/transparent_disputed.rb +11 -0
- data/lib/pay/abacatepay/webhooks/transparent_lost.rb +11 -0
- data/lib/pay/abacatepay/webhooks/transparent_refunded.rb +11 -0
- data/lib/pay/abacatepay/webhooks.rb +23 -0
- data/lib/pay/abacatepay.rb +75 -0
- data/lib/pay-abacatepay.rb +1 -0
- metadata +105 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Pay
|
|
2
|
+
module Abacatepay
|
|
3
|
+
# Stub STI class for Pay::PaymentMethod scoped to AbacatePay.
|
|
4
|
+
#
|
|
5
|
+
# AbacatePay does not (yet) expose tokenized payment methods that we can
|
|
6
|
+
# store and reuse — every transparent transaction needs full card data.
|
|
7
|
+
# This class exists so that:
|
|
8
|
+
#
|
|
9
|
+
# 1. `Pay::Abacatepay::Customer has_many :payment_methods` resolves
|
|
10
|
+
# correctly (avoids `Missing model class` error on customer destroy).
|
|
11
|
+
# 2. We can persist tokenized payment methods later without breaking
|
|
12
|
+
# existing data when transparent checkout lands (Fase 5).
|
|
13
|
+
#
|
|
14
|
+
# Until transparent checkout is implemented, instances of this class
|
|
15
|
+
# are not created — the relation is empty for every customer.
|
|
16
|
+
class PaymentMethod < Pay::PaymentMethod
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
ActiveSupport.run_load_hooks :pay_abacatepay_payment_method, Pay::Abacatepay::PaymentMethod
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Pay
|
|
2
|
+
module Abacatepay
|
|
3
|
+
class ProcessedWebhook < ActiveRecord::Base
|
|
4
|
+
self.table_name = "pay_abacatepay_processed_webhooks"
|
|
5
|
+
|
|
6
|
+
validates :event_type, :event_id, presence: true
|
|
7
|
+
|
|
8
|
+
def self.process!(event_type:, event_id:)
|
|
9
|
+
transaction(requires_new: true) do
|
|
10
|
+
create!(event_type: event_type, event_id: event_id, processed_at: Time.current)
|
|
11
|
+
yield if block_given?
|
|
12
|
+
end
|
|
13
|
+
nil
|
|
14
|
+
rescue ActiveRecord::RecordNotUnique
|
|
15
|
+
:already_processed
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
module Pay
|
|
2
|
+
module Abacatepay
|
|
3
|
+
class Subscription < Pay::Subscription
|
|
4
|
+
STATUS_FROM_EVENT = {
|
|
5
|
+
"subscription.completed" => "active",
|
|
6
|
+
"subscription.renewed" => "active",
|
|
7
|
+
"subscription.cancelled" => "canceled"
|
|
8
|
+
}.freeze
|
|
9
|
+
|
|
10
|
+
def self.sync(processor_id, event: nil)
|
|
11
|
+
return if processor_id.blank?
|
|
12
|
+
|
|
13
|
+
subscription = Pay::Abacatepay::Subscription.find_by(processor_id: processor_id)
|
|
14
|
+
customer = subscription&.customer
|
|
15
|
+
customer ||= Pay::Customer.find_by(processor: "abacatepay", processor_id: event&.customer_id) if event
|
|
16
|
+
|
|
17
|
+
if customer.nil?
|
|
18
|
+
Rails.logger.warn("[pay-abacatepay] cannot sync subscription #{processor_id}: Pay::Customer not found")
|
|
19
|
+
return
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
subscription ||= Pay::Abacatepay::Subscription.new(customer: customer, processor_id: processor_id)
|
|
23
|
+
assign_attributes_from(subscription, event) if event
|
|
24
|
+
subscription.save!
|
|
25
|
+
subscription
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.assign_attributes_from(subscription, event)
|
|
29
|
+
status = STATUS_FROM_EVENT[event.type] || subscription.status || "active"
|
|
30
|
+
period_start = event.paid_at || event.subscription_created_at || Time.current
|
|
31
|
+
interval = event.interval
|
|
32
|
+
|
|
33
|
+
subscription.assign_attributes(
|
|
34
|
+
name: event.product_id || subscription.name || "default",
|
|
35
|
+
processor_plan: event.product_id || subscription.processor_plan || "default",
|
|
36
|
+
status: status,
|
|
37
|
+
current_period_start: period_start,
|
|
38
|
+
current_period_end: interval ? period_start + interval : subscription.current_period_end,
|
|
39
|
+
ends_at: (status == "canceled") ? (event.canceled_at || Time.current) : subscription.ends_at
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
private_class_method :assign_attributes_from
|
|
43
|
+
|
|
44
|
+
def cancel(**options)
|
|
45
|
+
Rails.logger.warn(
|
|
46
|
+
"[pay-abacatepay] AbacatePay does not support cancel-at-period-end; cancelling immediately"
|
|
47
|
+
)
|
|
48
|
+
cancel_now!(**options)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def cancel_now!(**_options)
|
|
52
|
+
::AbacatePay.subscriptions.send(:request, "POST", "cancel", params: {id: processor_id})
|
|
53
|
+
update!(status: "canceled", ends_at: Time.current)
|
|
54
|
+
rescue ::AbacatePay::Error => e
|
|
55
|
+
raise Pay::Abacatepay::Error, e.message
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def resume
|
|
59
|
+
raise NotImplementedError,
|
|
60
|
+
"AbacatePay does not support resuming cancelled subscriptions; create a new subscription"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def swap(_plan, **_options)
|
|
64
|
+
raise NotImplementedError,
|
|
65
|
+
"AbacatePay does not support plan swap; cancel the subscription and create a new one"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def change_quantity(_quantity, **_options)
|
|
69
|
+
raise NotImplementedError,
|
|
70
|
+
"AbacatePay does not support quantity changes"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# AbacatePay does not emit payment-failure events, so we cannot observe
|
|
74
|
+
# a past_due state. Always false.
|
|
75
|
+
def past_due?
|
|
76
|
+
false
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def api_record
|
|
80
|
+
::AbacatePay.subscriptions.send(:request, "GET", "get", params: {id: processor_id})
|
|
81
|
+
rescue ::AbacatePay::Error => e
|
|
82
|
+
raise Pay::Abacatepay::Error, e.message
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
ActiveSupport.run_load_hooks :pay_abacatepay_subscription, Pay::Abacatepay::Subscription
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Pay
|
|
2
|
+
module Abacatepay
|
|
3
|
+
module Webhooks
|
|
4
|
+
class CheckoutCompleted
|
|
5
|
+
def call(event_hash)
|
|
6
|
+
event = Pay::Abacatepay::Webhooks::Event.new(event_hash)
|
|
7
|
+
result = Pay::Abacatepay::ProcessedWebhook.process!(event_type: event.type, event_id: event.id) do
|
|
8
|
+
handle(event)
|
|
9
|
+
end
|
|
10
|
+
log_already_processed(event) if result == :already_processed
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def handle(event)
|
|
16
|
+
if event.checkout_frequency && event.checkout_frequency != "ONE_TIME"
|
|
17
|
+
Rails.logger.debug(
|
|
18
|
+
"[pay-abacatepay] checkout.completed for #{event.checkout_id} is a subscription payment " \
|
|
19
|
+
"(frequency=#{event.checkout_frequency}); skipping — subscription.renewed handles it"
|
|
20
|
+
)
|
|
21
|
+
return
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
customer = Pay::Customer.find_by(processor: "abacatepay", processor_id: event.customer_id)
|
|
25
|
+
if customer.nil?
|
|
26
|
+
Rails.logger.warn(
|
|
27
|
+
"[pay-abacatepay] checkout.completed for unknown customer #{event.customer_id}; " \
|
|
28
|
+
"Pay::Customer must be created via the app signup flow before the webhook arrives"
|
|
29
|
+
)
|
|
30
|
+
return
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
charge = Pay::Abacatepay::Charge.find_or_initialize_by(
|
|
34
|
+
customer: customer,
|
|
35
|
+
processor_id: event.checkout_id
|
|
36
|
+
)
|
|
37
|
+
charge.amount = event.checkout_paid_amount_cents || event.checkout_amount_cents
|
|
38
|
+
charge.currency ||= "BRL"
|
|
39
|
+
charge.application_fee_amount = event.checkout_platform_fee_cents
|
|
40
|
+
charge.status = "paid"
|
|
41
|
+
charge.checkout_url = event.checkout_url if event.checkout_url
|
|
42
|
+
charge.created_at = event.paid_at if charge.new_record? && event.paid_at
|
|
43
|
+
charge.save!
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def log_already_processed(event)
|
|
47
|
+
Rails.logger.info("[pay-abacatepay] #{event.type} #{event.id} already processed")
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Pay
|
|
2
|
+
module Abacatepay
|
|
3
|
+
module Webhooks
|
|
4
|
+
class CheckoutRefunded
|
|
5
|
+
def call(event_hash)
|
|
6
|
+
event = Pay::Abacatepay::Webhooks::Event.new(event_hash)
|
|
7
|
+
result = Pay::Abacatepay::ProcessedWebhook.process!(event_type: event.type, event_id: event.id) do
|
|
8
|
+
handle(event)
|
|
9
|
+
end
|
|
10
|
+
log_already_processed(event) if result == :already_processed
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def handle(event)
|
|
16
|
+
charge = Pay::Abacatepay::Charge.find_by(processor_id: event.checkout_id)
|
|
17
|
+
if charge.nil?
|
|
18
|
+
Rails.logger.warn(
|
|
19
|
+
"[pay-abacatepay] checkout.refunded for unknown checkout #{event.checkout_id}; " \
|
|
20
|
+
"no Pay::Abacatepay::Charge to update"
|
|
21
|
+
)
|
|
22
|
+
return
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
refund_amount = event.checkout_paid_amount_cents || charge.amount
|
|
26
|
+
charge.update!(
|
|
27
|
+
amount_refunded: refund_amount,
|
|
28
|
+
status: "refunded"
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def log_already_processed(event)
|
|
33
|
+
Rails.logger.info("[pay-abacatepay] #{event.type} #{event.id} already processed")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
module Pay
|
|
2
|
+
module Abacatepay
|
|
3
|
+
module Webhooks
|
|
4
|
+
class Event
|
|
5
|
+
FREQUENCY_TO_INTERVAL = {
|
|
6
|
+
"WEEKLY" => 1.week,
|
|
7
|
+
"MONTHLY" => 1.month,
|
|
8
|
+
"SEMIANNUALLY" => 6.months,
|
|
9
|
+
"ANNUALLY" => 1.year
|
|
10
|
+
}.freeze
|
|
11
|
+
|
|
12
|
+
def initialize(raw)
|
|
13
|
+
@raw = raw.is_a?(Hash) ? raw : {}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
attr_reader :raw
|
|
17
|
+
|
|
18
|
+
def id = @raw["id"]
|
|
19
|
+
|
|
20
|
+
def type = @raw["event"] || @raw["type"]
|
|
21
|
+
|
|
22
|
+
def data = @raw["data"] || {}
|
|
23
|
+
|
|
24
|
+
def api_version = @raw["apiVersion"]
|
|
25
|
+
|
|
26
|
+
def dev_mode? = @raw["devMode"] == true
|
|
27
|
+
|
|
28
|
+
def subscription_id = data.dig("subscription", "id")
|
|
29
|
+
|
|
30
|
+
def subscription_status = data.dig("subscription", "status")
|
|
31
|
+
|
|
32
|
+
def subscription_amount_cents = data.dig("subscription", "amount")
|
|
33
|
+
|
|
34
|
+
def subscription_currency = data.dig("subscription", "currency") || "BRL"
|
|
35
|
+
|
|
36
|
+
def frequency = data.dig("subscription", "frequency")
|
|
37
|
+
|
|
38
|
+
def subscription_created_at = parse_time(data.dig("subscription", "createdAt"))
|
|
39
|
+
|
|
40
|
+
def subscription_updated_at = parse_time(data.dig("subscription", "updatedAt"))
|
|
41
|
+
|
|
42
|
+
def canceled_at = parse_time(data.dig("subscription", "canceledAt"))
|
|
43
|
+
|
|
44
|
+
def customer_id = data.dig("customer", "id")
|
|
45
|
+
|
|
46
|
+
def customer_email = data.dig("customer", "email")
|
|
47
|
+
|
|
48
|
+
def customer_name = data.dig("customer", "name")
|
|
49
|
+
|
|
50
|
+
def customer_tax_id = data.dig("customer", "taxId")
|
|
51
|
+
|
|
52
|
+
def charge_id = data.dig("payment", "id")
|
|
53
|
+
|
|
54
|
+
def charge_amount_cents = data.dig("payment", "amount")
|
|
55
|
+
|
|
56
|
+
def paid_amount_cents = data.dig("payment", "paidAmount")
|
|
57
|
+
|
|
58
|
+
def platform_fee_cents = data.dig("payment", "platformFee")
|
|
59
|
+
|
|
60
|
+
def charge_status = data.dig("payment", "status")
|
|
61
|
+
|
|
62
|
+
def paid_at = parse_time(data.dig("payment", "updatedAt"))
|
|
63
|
+
|
|
64
|
+
def checkout_id = data.dig("checkout", "id")
|
|
65
|
+
|
|
66
|
+
def checkout_frequency = data.dig("checkout", "frequency")
|
|
67
|
+
|
|
68
|
+
def checkout_status = data.dig("checkout", "status")
|
|
69
|
+
|
|
70
|
+
def checkout_url = data.dig("checkout", "url")
|
|
71
|
+
|
|
72
|
+
def checkout_amount_cents = data.dig("checkout", "amount")
|
|
73
|
+
|
|
74
|
+
def checkout_paid_amount_cents = data.dig("checkout", "paidAmount")
|
|
75
|
+
|
|
76
|
+
def checkout_platform_fee_cents = data.dig("checkout", "platformFee")
|
|
77
|
+
|
|
78
|
+
def checkout_methods = data.dig("checkout", "methods")
|
|
79
|
+
|
|
80
|
+
def product_id = data.dig("checkout", "items", 0, "id")
|
|
81
|
+
|
|
82
|
+
def interval
|
|
83
|
+
FREQUENCY_TO_INTERVAL[frequency]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
def parse_time(value)
|
|
89
|
+
return nil if value.nil? || value.to_s.empty?
|
|
90
|
+
Time.zone ? Time.zone.parse(value.to_s) : Time.parse(value.to_s)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Pay
|
|
2
|
+
module Abacatepay
|
|
3
|
+
module Webhooks
|
|
4
|
+
class SubscriptionCancelled
|
|
5
|
+
def call(event_hash)
|
|
6
|
+
event = Pay::Abacatepay::Webhooks::Event.new(event_hash)
|
|
7
|
+
result = Pay::Abacatepay::ProcessedWebhook.process!(event_type: event.type, event_id: event.id) do
|
|
8
|
+
handle(event)
|
|
9
|
+
end
|
|
10
|
+
log_already_processed(event) if result == :already_processed
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def handle(event)
|
|
16
|
+
subscription = Pay::Abacatepay::Subscription.find_by(processor_id: event.subscription_id)
|
|
17
|
+
|
|
18
|
+
if subscription.nil?
|
|
19
|
+
Rails.logger.warn(
|
|
20
|
+
"[pay-abacatepay] subscription.cancelled for unknown subscription #{event.subscription_id}; no-op"
|
|
21
|
+
)
|
|
22
|
+
return
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
subscription.update!(status: "canceled", ends_at: event.canceled_at || Time.current)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def log_already_processed(event)
|
|
29
|
+
Rails.logger.info("[pay-abacatepay] #{event.type} #{event.id} already processed")
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Pay
|
|
2
|
+
module Abacatepay
|
|
3
|
+
module Webhooks
|
|
4
|
+
class SubscriptionCompleted
|
|
5
|
+
def call(event_hash)
|
|
6
|
+
event = Pay::Abacatepay::Webhooks::Event.new(event_hash)
|
|
7
|
+
result = Pay::Abacatepay::ProcessedWebhook.process!(event_type: event.type, event_id: event.id) do
|
|
8
|
+
handle(event)
|
|
9
|
+
end
|
|
10
|
+
log_already_processed(event) if result == :already_processed
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def handle(event)
|
|
16
|
+
customer = find_customer(event)
|
|
17
|
+
return if customer.nil?
|
|
18
|
+
|
|
19
|
+
subscription = Pay::Abacatepay::Subscription.sync(event.subscription_id, event: event)
|
|
20
|
+
create_initial_charge(customer, subscription, event) if subscription && event.charge_id
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def find_customer(event)
|
|
24
|
+
return nil if event.customer_id.blank?
|
|
25
|
+
|
|
26
|
+
customer = Pay::Customer.find_by(processor: "abacatepay", processor_id: event.customer_id)
|
|
27
|
+
if customer.nil?
|
|
28
|
+
Rails.logger.warn(
|
|
29
|
+
"[pay-abacatepay] subscription.completed for unknown customer #{event.customer_id}; " \
|
|
30
|
+
"Pay::Customer must be created via the app signup flow before the webhook arrives"
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
customer
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def create_initial_charge(customer, subscription, event)
|
|
37
|
+
Pay::Abacatepay::Charge.find_or_create_by!(customer: customer, processor_id: event.charge_id) do |charge|
|
|
38
|
+
charge.amount = event.paid_amount_cents || event.charge_amount_cents
|
|
39
|
+
charge.currency = event.subscription_currency
|
|
40
|
+
charge.application_fee_amount = event.platform_fee_cents
|
|
41
|
+
charge.subscription_id = subscription.id
|
|
42
|
+
charge.status = "paid"
|
|
43
|
+
charge.created_at = event.paid_at if event.paid_at
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def log_already_processed(event)
|
|
48
|
+
Rails.logger.info("[pay-abacatepay] #{event.type} #{event.id} already processed")
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Pay
|
|
2
|
+
module Abacatepay
|
|
3
|
+
module Webhooks
|
|
4
|
+
class SubscriptionRenewed
|
|
5
|
+
def call(event_hash)
|
|
6
|
+
event = Pay::Abacatepay::Webhooks::Event.new(event_hash)
|
|
7
|
+
result = Pay::Abacatepay::ProcessedWebhook.process!(event_type: event.type, event_id: event.id) do
|
|
8
|
+
handle(event)
|
|
9
|
+
end
|
|
10
|
+
log_already_processed(event) if result == :already_processed
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def handle(event)
|
|
16
|
+
existing = Pay::Abacatepay::Subscription.find_by(processor_id: event.subscription_id)
|
|
17
|
+
|
|
18
|
+
if existing.nil?
|
|
19
|
+
Rails.logger.warn(
|
|
20
|
+
"[pay-abacatepay] subscription.renewed before subscription.completed for #{event.subscription_id}; " \
|
|
21
|
+
"creating subscription on-the-fly"
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
subscription = Pay::Abacatepay::Subscription.sync(event.subscription_id, event: event)
|
|
26
|
+
return if subscription.nil?
|
|
27
|
+
return if event.charge_id.blank?
|
|
28
|
+
|
|
29
|
+
Pay::Abacatepay::Charge.find_or_create_by!(customer: subscription.customer, processor_id: event.charge_id) do |charge|
|
|
30
|
+
charge.amount = event.paid_amount_cents || event.charge_amount_cents
|
|
31
|
+
charge.currency = event.subscription_currency
|
|
32
|
+
charge.application_fee_amount = event.platform_fee_cents
|
|
33
|
+
charge.subscription_id = subscription.id
|
|
34
|
+
charge.status = "paid"
|
|
35
|
+
charge.created_at = event.paid_at if event.paid_at
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def log_already_processed(event)
|
|
40
|
+
Rails.logger.info("[pay-abacatepay] #{event.type} #{event.id} already processed")
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Pay
|
|
2
|
+
module Abacatepay
|
|
3
|
+
module Webhooks
|
|
4
|
+
class SubscriptionTrialStarted
|
|
5
|
+
def call(_event_hash)
|
|
6
|
+
raise NotImplementedError,
|
|
7
|
+
"subscription.trial_started is not listed in AbacatePay::Enums::Webhooks::EventTypes. " \
|
|
8
|
+
"Confirm the event exists in the AbacatePay API before enabling trial support."
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Pay
|
|
2
|
+
module Abacatepay
|
|
3
|
+
module Webhooks
|
|
4
|
+
autoload :Event, "pay/abacatepay/webhooks/event"
|
|
5
|
+
autoload :CheckoutCompleted, "pay/abacatepay/webhooks/checkout_completed"
|
|
6
|
+
autoload :CheckoutRefunded, "pay/abacatepay/webhooks/checkout_refunded"
|
|
7
|
+
autoload :CheckoutDisputed, "pay/abacatepay/webhooks/checkout_disputed"
|
|
8
|
+
autoload :CheckoutLost, "pay/abacatepay/webhooks/checkout_lost"
|
|
9
|
+
autoload :TransparentCompleted, "pay/abacatepay/webhooks/transparent_completed"
|
|
10
|
+
autoload :TransparentRefunded, "pay/abacatepay/webhooks/transparent_refunded"
|
|
11
|
+
autoload :TransparentDisputed, "pay/abacatepay/webhooks/transparent_disputed"
|
|
12
|
+
autoload :TransparentLost, "pay/abacatepay/webhooks/transparent_lost"
|
|
13
|
+
autoload :SubscriptionCompleted, "pay/abacatepay/webhooks/subscription_completed"
|
|
14
|
+
autoload :SubscriptionCancelled, "pay/abacatepay/webhooks/subscription_cancelled"
|
|
15
|
+
autoload :SubscriptionRenewed, "pay/abacatepay/webhooks/subscription_renewed"
|
|
16
|
+
autoload :SubscriptionTrialStarted, "pay/abacatepay/webhooks/subscription_trial_started"
|
|
17
|
+
autoload :PayoutCompleted, "pay/abacatepay/webhooks/payout_completed"
|
|
18
|
+
autoload :PayoutFailed, "pay/abacatepay/webhooks/payout_failed"
|
|
19
|
+
autoload :TransferCompleted, "pay/abacatepay/webhooks/transfer_completed"
|
|
20
|
+
autoload :TransferFailed, "pay/abacatepay/webhooks/transfer_failed"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|