payflow 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +116 -0
- data/app/controllers/payflow/application_controller.rb +6 -0
- data/app/controllers/payflow/webhooks/asaas_controller.rb +18 -0
- data/app/controllers/payflow/webhooks/stripe_controller.rb +18 -0
- data/app/jobs/payflow/application_job.rb +6 -0
- data/app/jobs/payflow/overdue_accounts_job.rb +16 -0
- data/app/jobs/payflow/webhook_job.rb +21 -0
- data/app/models/payflow/application_record.rb +7 -0
- data/app/models/payflow/invoice.rb +18 -0
- data/app/models/payflow/subscription.rb +32 -0
- data/app/models/payflow/webhook_event.rb +19 -0
- data/config/routes.rb +8 -0
- data/db/migrate/20260617180001_create_payflow_subscriptions.rb +19 -0
- data/db/migrate/20260617180002_create_payflow_invoices.rb +18 -0
- data/db/migrate/20260617180003_create_payflow_webhook_events.rb +17 -0
- data/lib/generators/payflow/install_generator.rb +40 -0
- data/lib/generators/payflow/templates/initializer.rb +13 -0
- data/lib/generators/payflow/templates/migration_invoice.rb +16 -0
- data/lib/generators/payflow/templates/migration_subscription.rb +17 -0
- data/lib/generators/payflow/templates/migration_webhook_event.rb +15 -0
- data/lib/payflow/billable.rb +34 -0
- data/lib/payflow/configuration.rb +31 -0
- data/lib/payflow/engine.rb +23 -0
- data/lib/payflow/errors.rb +13 -0
- data/lib/payflow/events.rb +9 -0
- data/lib/payflow/provider_resolver.rb +18 -0
- data/lib/payflow/providers/asaas/customer.rb +17 -0
- data/lib/payflow/providers/asaas/provider.rb +41 -0
- data/lib/payflow/providers/asaas/subscription.rb +50 -0
- data/lib/payflow/providers/asaas/webhook.rb +43 -0
- data/lib/payflow/providers/base.rb +51 -0
- data/lib/payflow/providers/stripe/provider.rb +40 -0
- data/lib/payflow/providers/stripe/subscription.rb +28 -0
- data/lib/payflow/providers/stripe/webhook.rb +31 -0
- data/lib/payflow/subscription_service.rb +48 -0
- data/lib/payflow/version.rb +5 -0
- data/lib/payflow/webhooks/dispatcher.rb +50 -0
- data/lib/payflow/webhooks/signature_verifier.rb +57 -0
- data/lib/payflow.rb +39 -0
- data/lib/tasks/payflow_tasks.rake +28 -0
- metadata +184 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "customer"
|
|
4
|
+
require_relative "subscription"
|
|
5
|
+
require_relative "webhook"
|
|
6
|
+
|
|
7
|
+
module Payflow
|
|
8
|
+
module Providers
|
|
9
|
+
module Asaas
|
|
10
|
+
class Provider < Base
|
|
11
|
+
def initialize
|
|
12
|
+
super(provider_name: :asaas)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def create_customer(attrs)
|
|
16
|
+
Customer.new(self).create(attrs)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def create_subscription(customer_id:, plan_id:, **options)
|
|
20
|
+
Subscription.new(self).create(customer_id: customer_id, plan_id: plan_id, **options)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def cancel_subscription(provider_subscription_id)
|
|
24
|
+
Subscription.new(self).cancel(provider_subscription_id)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def list_invoices(provider_subscription_id:)
|
|
28
|
+
[]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def verify_webhook_signature(payload:, headers:)
|
|
32
|
+
Webhook.new(self).verify_signature(payload: payload, headers: headers)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def parse_webhook(payload:, headers: {})
|
|
36
|
+
Webhook.new(self).parse(payload: payload, headers: headers)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Payflow
|
|
4
|
+
module Providers
|
|
5
|
+
module Asaas
|
|
6
|
+
class Subscription
|
|
7
|
+
BASE_URL = "https://api.asaas.com/v3"
|
|
8
|
+
|
|
9
|
+
def initialize(client)
|
|
10
|
+
@client = client
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def create(customer_id:, plan_id:, **)
|
|
14
|
+
response = http_client.post("/subscriptions") do |req|
|
|
15
|
+
req.body = {
|
|
16
|
+
customer: customer_id,
|
|
17
|
+
billingType: "CREDIT_CARD",
|
|
18
|
+
value: plan_value(plan_id),
|
|
19
|
+
cycle: "MONTHLY",
|
|
20
|
+
description: "Plan #{plan_id}"
|
|
21
|
+
}.to_json
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
data = JSON.parse(response.body)
|
|
25
|
+
{ id: data["id"], provider: :asaas, status: "active" }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def cancel(provider_subscription_id)
|
|
29
|
+
http_client.delete("/subscriptions/#{provider_subscription_id}")
|
|
30
|
+
{ id: provider_subscription_id, provider: :asaas, status: "cancelled" }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def http_client
|
|
36
|
+
@http_client ||= Faraday.new(url: BASE_URL) do |f|
|
|
37
|
+
f.request :json
|
|
38
|
+
f.response :raise_error
|
|
39
|
+
f.headers["access_token"] = @client.send(:credentials)[:api_key]
|
|
40
|
+
f.headers["Content-Type"] = "application/json"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def plan_value(plan)
|
|
45
|
+
{ "basic" => 29.90, "premium" => 99.90 }[plan.to_s] || 49.90
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Payflow
|
|
4
|
+
module Providers
|
|
5
|
+
module Asaas
|
|
6
|
+
class Webhook
|
|
7
|
+
ASAAS_TOKEN_HEADER = "asaas-access-token"
|
|
8
|
+
|
|
9
|
+
def initialize(client)
|
|
10
|
+
@client = client
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def verify_signature(payload:, headers:)
|
|
14
|
+
token = headers[ASAAS_TOKEN_HEADER] || headers[ASAAS_TOKEN_HEADER.upcase]
|
|
15
|
+
expected = @client.send(:credentials)[:webhook_token]
|
|
16
|
+
return false if expected.blank? || token.blank?
|
|
17
|
+
ActiveSupport::SecurityUtils.secure_compare(expected.to_s, token.to_s)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def parse(payload:, headers: {})
|
|
21
|
+
body = payload.is_a?(String) ? JSON.parse(payload) : payload
|
|
22
|
+
{
|
|
23
|
+
idempotency_key: body["id"] || body.dig("payment", "id"),
|
|
24
|
+
event_type: map_event_type(body["event"]),
|
|
25
|
+
provider: :asaas,
|
|
26
|
+
data: body
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def map_event_type(asaas_event)
|
|
33
|
+
case asaas_event
|
|
34
|
+
when "PAYMENT_RECEIVED" then Payflow::Events::INVOICE_PAID
|
|
35
|
+
when "SUBSCRIPTION_CREATED" then Payflow::Events::SUBSCRIPTION_CREATED
|
|
36
|
+
when "SUBSCRIPTION_DELETED" then Payflow::Events::SUBSCRIPTION_CANCELED
|
|
37
|
+
else asaas_event.to_s.downcase.tr("_", ".")
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Payflow
|
|
4
|
+
module Providers
|
|
5
|
+
class Base
|
|
6
|
+
attr_reader :provider_name
|
|
7
|
+
|
|
8
|
+
def initialize(provider_name:)
|
|
9
|
+
@provider_name = provider_name.to_sym
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def create_customer(attrs)
|
|
13
|
+
raise NotImplementedError, "#{self.class}##{__method__}"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def find_customer(provider_customer_id)
|
|
17
|
+
raise NotImplementedError, "#{self.class}##{__method__}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def create_subscription(customer_id:, plan_id:, **options)
|
|
21
|
+
raise NotImplementedError, "#{self.class}##{__method__}"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def cancel_subscription(provider_subscription_id)
|
|
25
|
+
raise NotImplementedError, "#{self.class}##{__method__}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def find_subscription(provider_subscription_id)
|
|
29
|
+
raise NotImplementedError, "#{self.class}##{__method__}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def list_invoices(provider_subscription_id:)
|
|
33
|
+
raise NotImplementedError, "#{self.class}##{__method__}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def verify_webhook_signature(payload:, headers:)
|
|
37
|
+
raise NotImplementedError, "#{self.class}##{__method__}"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def parse_webhook(payload:, headers: {})
|
|
41
|
+
raise NotImplementedError, "#{self.class}##{__method__}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
protected
|
|
45
|
+
|
|
46
|
+
def credentials
|
|
47
|
+
Payflow.config.provider_credentials(provider_name)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "subscription"
|
|
4
|
+
require_relative "webhook"
|
|
5
|
+
|
|
6
|
+
module Payflow
|
|
7
|
+
module Providers
|
|
8
|
+
module Stripe
|
|
9
|
+
class Provider < Base
|
|
10
|
+
def initialize
|
|
11
|
+
super(provider_name: :stripe)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def create_customer(attrs)
|
|
15
|
+
{ id: "cus_stripe_stub_#{SecureRandom.hex(4)}", provider: :stripe, email: attrs[:email], name: attrs[:name] }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def create_subscription(customer_id:, plan_id:, **options)
|
|
19
|
+
Subscription.new(self).create(customer_id: customer_id, plan_id: plan_id, **options)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def cancel_subscription(provider_subscription_id)
|
|
23
|
+
Subscription.new(self).cancel(provider_subscription_id)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def list_invoices(provider_subscription_id:)
|
|
27
|
+
[]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def verify_webhook_signature(payload:, headers:)
|
|
31
|
+
Webhook.new(self).verify_signature(payload: payload, headers: headers)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def parse_webhook(payload:, headers: {})
|
|
35
|
+
Webhook.new(self).parse(payload: payload, headers: headers)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Payflow
|
|
4
|
+
module Providers
|
|
5
|
+
module Stripe
|
|
6
|
+
class Subscription
|
|
7
|
+
def initialize(client)
|
|
8
|
+
@client = client
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def create(customer_id:, plan_id:, **options)
|
|
12
|
+
{
|
|
13
|
+
id: "sub_stripe_stub_#{SecureRandom.hex(4)}",
|
|
14
|
+
provider: :stripe,
|
|
15
|
+
customer_id: customer_id,
|
|
16
|
+
plan_id: plan_id,
|
|
17
|
+
status: "active",
|
|
18
|
+
current_period_end: options[:current_period_end]
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def cancel(provider_subscription_id)
|
|
23
|
+
{ id: provider_subscription_id, provider: :stripe, status: "canceled" }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Payflow
|
|
4
|
+
module Providers
|
|
5
|
+
module Stripe
|
|
6
|
+
class Webhook
|
|
7
|
+
def initialize(client)
|
|
8
|
+
@client = client
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def verify_signature(payload:, headers:)
|
|
12
|
+
Payflow::Webhooks::SignatureVerifier.verify_stripe(
|
|
13
|
+
payload: payload,
|
|
14
|
+
signature_header: headers["HTTP_STRIPE_SIGNATURE"] || headers["Stripe-Signature"],
|
|
15
|
+
secret: @client.send(:credentials)[:webhook_secret]
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def parse(payload:, headers: {})
|
|
20
|
+
body = payload.is_a?(String) ? JSON.parse(payload) : payload
|
|
21
|
+
{
|
|
22
|
+
idempotency_key: body["id"],
|
|
23
|
+
event_type: body["type"],
|
|
24
|
+
provider: :stripe,
|
|
25
|
+
data: body.dig("data", "object") || {}
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Payflow
|
|
4
|
+
class SubscriptionService
|
|
5
|
+
def initialize(billable:)
|
|
6
|
+
@billable = billable
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def subscribe!(plan:, provider: Payflow.config.provider)
|
|
10
|
+
current = @billable.subscription
|
|
11
|
+
raise SubscriptionError, "Already subscribed" if current&.active?
|
|
12
|
+
|
|
13
|
+
client = Payflow.provider(provider)
|
|
14
|
+
remote = client.create_subscription(
|
|
15
|
+
customer_id: customer_id_for(@billable),
|
|
16
|
+
plan_id: plan
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
Subscription.create!(
|
|
20
|
+
billable: @billable,
|
|
21
|
+
plan: plan,
|
|
22
|
+
provider: provider.to_s,
|
|
23
|
+
external_id: remote[:id],
|
|
24
|
+
status: :active
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def cancel!
|
|
29
|
+
subscription = @billable.subscription
|
|
30
|
+
raise SubscriptionError, "No active subscription" unless subscription
|
|
31
|
+
|
|
32
|
+
client = Payflow.provider(subscription.provider.to_sym)
|
|
33
|
+
client.cancel_subscription(subscription.external_id)
|
|
34
|
+
subscription.update!(status: :cancelled, cancelled_at: Time.current)
|
|
35
|
+
subscription
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def customer_id_for(billable)
|
|
41
|
+
if billable.respond_to?(:payflow_provider_customer_id)
|
|
42
|
+
billable.payflow_provider_customer_id
|
|
43
|
+
else
|
|
44
|
+
"cus_#{billable.class.name.underscore}_#{billable.id}"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Payflow
|
|
4
|
+
module Webhooks
|
|
5
|
+
class Dispatcher
|
|
6
|
+
PAYMENT_OVERDUE = "PAYMENT_OVERDUE"
|
|
7
|
+
PAYMENT_RECEIVED = "PAYMENT_RECEIVED"
|
|
8
|
+
SUBSCRIPTION_DELETED = "SUBSCRIPTION_DELETED"
|
|
9
|
+
|
|
10
|
+
def initialize(webhook_event:)
|
|
11
|
+
@webhook_event = webhook_event
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def dispatch!
|
|
15
|
+
payload = @webhook_event.payload
|
|
16
|
+
event_type = payload["event"]
|
|
17
|
+
|
|
18
|
+
case event_type
|
|
19
|
+
when PAYMENT_OVERDUE
|
|
20
|
+
mark_subscription_overdue(payload)
|
|
21
|
+
when PAYMENT_RECEIVED
|
|
22
|
+
mark_subscription_active(payload)
|
|
23
|
+
when SUBSCRIPTION_DELETED
|
|
24
|
+
mark_subscription_cancelled(payload)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def find_subscription(payload)
|
|
31
|
+
external_id = payload.dig("payment", "subscription") || payload.dig("subscription", "id")
|
|
32
|
+
return unless external_id
|
|
33
|
+
|
|
34
|
+
Subscription.find_by(external_id: external_id)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def mark_subscription_overdue(payload)
|
|
38
|
+
find_subscription(payload)&.update!(status: :overdue)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def mark_subscription_active(payload)
|
|
42
|
+
find_subscription(payload)&.update!(status: :active)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def mark_subscription_cancelled(payload)
|
|
46
|
+
find_subscription(payload)&.update!(status: :cancelled, cancelled_at: Time.current)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Payflow
|
|
4
|
+
module Webhooks
|
|
5
|
+
class SignatureVerifier
|
|
6
|
+
def initialize(provider:, request:)
|
|
7
|
+
@provider = provider.to_sym
|
|
8
|
+
@request = request
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def valid?
|
|
12
|
+
case @provider
|
|
13
|
+
when :asaas
|
|
14
|
+
verify_asaas
|
|
15
|
+
when :stripe
|
|
16
|
+
verify_stripe
|
|
17
|
+
else
|
|
18
|
+
false
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.verify_stripe(payload:, signature_header:, secret:)
|
|
23
|
+
return false if secret.blank? || signature_header.blank?
|
|
24
|
+
|
|
25
|
+
timestamp, signature = extract_stripe_parts(signature_header)
|
|
26
|
+
return false unless timestamp && signature
|
|
27
|
+
|
|
28
|
+
signed_payload = "#{timestamp}.#{payload}"
|
|
29
|
+
expected = OpenSSL::HMAC.hexdigest("SHA256", secret, signed_payload)
|
|
30
|
+
ActiveSupport::SecurityUtils.secure_compare(expected, signature)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.extract_stripe_parts(header)
|
|
34
|
+
parts = header.split(",").to_h { |part| part.split("=", 2) }
|
|
35
|
+
[parts["t"], parts["v1"]]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def verify_asaas
|
|
41
|
+
token = @request.headers["asaas-access-token"]
|
|
42
|
+
expected = Payflow.config.asaas_webhook_token
|
|
43
|
+
return false if token.blank? || expected.blank?
|
|
44
|
+
|
|
45
|
+
ActiveSupport::SecurityUtils.secure_compare(token, expected)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def verify_stripe
|
|
49
|
+
self.class.verify_stripe(
|
|
50
|
+
payload: @request.raw_post,
|
|
51
|
+
signature_header: @request.headers["HTTP_STRIPE_SIGNATURE"] || @request.headers["Stripe-Signature"],
|
|
52
|
+
secret: Payflow.config.stripe_webhook_secret
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
data/lib/payflow.rb
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails"
|
|
4
|
+
require "active_job/railtie"
|
|
5
|
+
require "active_record/railtie"
|
|
6
|
+
require "action_controller/railtie"
|
|
7
|
+
require "faraday"
|
|
8
|
+
require "sidekiq"
|
|
9
|
+
|
|
10
|
+
require "payflow/version"
|
|
11
|
+
require "payflow/errors"
|
|
12
|
+
require "payflow/events"
|
|
13
|
+
require "payflow/configuration"
|
|
14
|
+
require "payflow/providers/base"
|
|
15
|
+
require "payflow/providers/asaas/provider"
|
|
16
|
+
require "payflow/providers/stripe/provider"
|
|
17
|
+
require "payflow/provider_resolver"
|
|
18
|
+
require "payflow/subscription_service"
|
|
19
|
+
require "payflow/billable"
|
|
20
|
+
require "payflow/webhooks/signature_verifier"
|
|
21
|
+
require "payflow/webhooks/dispatcher"
|
|
22
|
+
require "payflow/engine"
|
|
23
|
+
|
|
24
|
+
module Payflow
|
|
25
|
+
class << self
|
|
26
|
+
def config
|
|
27
|
+
@config ||= Configuration.new
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def configure
|
|
31
|
+
yield config if block_given?
|
|
32
|
+
config
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def provider(name = nil)
|
|
36
|
+
ProviderResolver.for(name || config.provider)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "payflow/version"
|
|
4
|
+
|
|
5
|
+
module Payflow
|
|
6
|
+
module Tasks
|
|
7
|
+
extend Rake::DSL
|
|
8
|
+
|
|
9
|
+
namespace :payflow do
|
|
10
|
+
namespace :install do
|
|
11
|
+
desc "Copy migrations from Payflow to host application"
|
|
12
|
+
task migrations: :environment do
|
|
13
|
+
engine = Payflow::Engine
|
|
14
|
+
source = engine.root.join("db/migrate")
|
|
15
|
+
destination = Rails.root.join("db/migrate")
|
|
16
|
+
|
|
17
|
+
Dir.glob(source.join("*.rb")).each do |migration|
|
|
18
|
+
target = destination.join(File.basename(migration))
|
|
19
|
+
next if target.exist?
|
|
20
|
+
|
|
21
|
+
FileUtils.cp(migration, target)
|
|
22
|
+
puts "Copied #{File.basename(migration)}"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|