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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +116 -0
  4. data/app/controllers/payflow/application_controller.rb +6 -0
  5. data/app/controllers/payflow/webhooks/asaas_controller.rb +18 -0
  6. data/app/controllers/payflow/webhooks/stripe_controller.rb +18 -0
  7. data/app/jobs/payflow/application_job.rb +6 -0
  8. data/app/jobs/payflow/overdue_accounts_job.rb +16 -0
  9. data/app/jobs/payflow/webhook_job.rb +21 -0
  10. data/app/models/payflow/application_record.rb +7 -0
  11. data/app/models/payflow/invoice.rb +18 -0
  12. data/app/models/payflow/subscription.rb +32 -0
  13. data/app/models/payflow/webhook_event.rb +19 -0
  14. data/config/routes.rb +8 -0
  15. data/db/migrate/20260617180001_create_payflow_subscriptions.rb +19 -0
  16. data/db/migrate/20260617180002_create_payflow_invoices.rb +18 -0
  17. data/db/migrate/20260617180003_create_payflow_webhook_events.rb +17 -0
  18. data/lib/generators/payflow/install_generator.rb +40 -0
  19. data/lib/generators/payflow/templates/initializer.rb +13 -0
  20. data/lib/generators/payflow/templates/migration_invoice.rb +16 -0
  21. data/lib/generators/payflow/templates/migration_subscription.rb +17 -0
  22. data/lib/generators/payflow/templates/migration_webhook_event.rb +15 -0
  23. data/lib/payflow/billable.rb +34 -0
  24. data/lib/payflow/configuration.rb +31 -0
  25. data/lib/payflow/engine.rb +23 -0
  26. data/lib/payflow/errors.rb +13 -0
  27. data/lib/payflow/events.rb +9 -0
  28. data/lib/payflow/provider_resolver.rb +18 -0
  29. data/lib/payflow/providers/asaas/customer.rb +17 -0
  30. data/lib/payflow/providers/asaas/provider.rb +41 -0
  31. data/lib/payflow/providers/asaas/subscription.rb +50 -0
  32. data/lib/payflow/providers/asaas/webhook.rb +43 -0
  33. data/lib/payflow/providers/base.rb +51 -0
  34. data/lib/payflow/providers/stripe/provider.rb +40 -0
  35. data/lib/payflow/providers/stripe/subscription.rb +28 -0
  36. data/lib/payflow/providers/stripe/webhook.rb +31 -0
  37. data/lib/payflow/subscription_service.rb +48 -0
  38. data/lib/payflow/version.rb +5 -0
  39. data/lib/payflow/webhooks/dispatcher.rb +50 -0
  40. data/lib/payflow/webhooks/signature_verifier.rb +57 -0
  41. data/lib/payflow.rb +39 -0
  42. data/lib/tasks/payflow_tasks.rake +28 -0
  43. metadata +184 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 97e6993b930178e108e6b1b805433af2216373def14db6abc037f44eab831b7d
4
+ data.tar.gz: 2de44cf55db703c4da5475a09d4bb872f35ac3745fe4bedf82b6813b5a90bb53
5
+ SHA512:
6
+ metadata.gz: 3392f52cb1c7e66a99da3d003987e9750070dd6b8653ee807a74f277f55570d56a6e2f35504646b62df6d13f38e97e53296fb21539f60499c8c80207a81c4b88
7
+ data.tar.gz: 7d071c31cb70af1b1157c49c87c208ffab0f10ae2ad23563859eb20a87e8440e8f47af56517b605e5bc267ad4b63fc2e61202be0194519c88ba121aaacd90dc7
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Conexus Systems
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,116 @@
1
+ # Payflow
2
+
3
+ Unified billing engine for Rails applications. Payflow abstracts subscription management and payment webhooks behind a provider-agnostic API.
4
+
5
+ **MVP 0.1.0 providers:** Asaas (functional), Stripe (basic).
6
+
7
+ Mercado Pago and Pagar.me are on the [roadmap](ROADMAP.md) — not included in this release.
8
+
9
+ ## Installation
10
+
11
+ Add to your Gemfile:
12
+
13
+ ```ruby
14
+ gem "payflow"
15
+ ```
16
+
17
+ Then run:
18
+
19
+ ```bash
20
+ bundle install
21
+ rails generate payflow:install
22
+ rails db:migrate
23
+ ```
24
+
25
+ Alternatively, copy engine migrations without the generator:
26
+
27
+ ```bash
28
+ rails payflow:install:migrations
29
+ rails db:migrate
30
+ ```
31
+
32
+ ## Setup
33
+
34
+ Create `config/initializers/payflow.rb` (or use the generator output):
35
+
36
+ ```ruby
37
+ Payflow.configure do |config|
38
+ config.provider = :asaas
39
+
40
+ config.asaas_api_key = ENV["ASAAS_API_KEY"]
41
+ config.asaas_webhook_token = ENV["ASAAS_WEBHOOK_TOKEN"]
42
+
43
+ config.stripe_api_key = ENV["STRIPE_API_KEY"]
44
+ config.stripe_webhook_secret = ENV["STRIPE_WEBHOOK_SECRET"]
45
+ end
46
+ ```
47
+
48
+ Mount the engine in `config/routes.rb`:
49
+
50
+ ```ruby
51
+ mount Payflow::Engine => "/payflow"
52
+ ```
53
+
54
+ ## Usage
55
+
56
+ Include `Payflow::Billable` in your host model:
57
+
58
+ ```ruby
59
+ class Organization < ApplicationRecord
60
+ include Payflow::Billable
61
+ end
62
+ ```
63
+
64
+ Subscribe and manage billing:
65
+
66
+ ```ruby
67
+ organization.subscribe!(plan: "premium", provider: Payflow.config.provider)
68
+ organization.cancel_subscription!
69
+ organization.active_subscription?
70
+ organization.can_access_system? # active and not overdue
71
+ ```
72
+
73
+ ## Webhooks
74
+
75
+ Configure your payment providers to send webhooks to:
76
+
77
+ | Provider | Endpoint |
78
+ |----------|----------|
79
+ | Asaas | `POST /payflow/webhooks/asaas` |
80
+ | Stripe | `POST /payflow/webhooks/stripe` |
81
+
82
+ Webhooks are verified, persisted as `Payflow::WebhookEvent` records, and processed asynchronously via `Payflow::WebhookJob` and Sidekiq (`:payflow` queue).
83
+
84
+ ## Architecture
85
+
86
+ ```
87
+ Host App
88
+ └── Payflow::Engine (mounted at /payflow)
89
+ ├── Payflow::Billable concern
90
+ ├── Models: Subscription, Invoice, WebhookEvent
91
+ ├── Jobs: WebhookJob, OverdueAccountsJob
92
+ ├── Payflow::ProviderResolver
93
+ │ ├── Providers::Asaas::Provider
94
+ │ └── Providers::Stripe::Provider
95
+ └── Webhooks::Dispatcher
96
+ ```
97
+
98
+ - **Billable** — polymorphic `billable` association on subscriptions
99
+ - **ProviderResolver** — `Payflow::ProviderResolver.for(:asaas)` returns the provider client
100
+ - **WebhookJob** — receives `provider` + `payload`, dispatches to update subscription/invoice state
101
+
102
+ ## Development
103
+
104
+ ```bash
105
+ bundle install
106
+ bundle exec rspec
107
+ bundle exec rubocop
108
+ ```
109
+
110
+ ## Releasing
111
+
112
+ See [RELEASE.md](RELEASE.md) for the RubyGems publishing checklist.
113
+
114
+ ## License
115
+
116
+ MIT — Copyright (c) Conexus Systems. See [LICENSE](LICENSE).
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Payflow
4
+ class ApplicationController < ActionController::API
5
+ end
6
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Payflow
4
+ module Webhooks
5
+ class AsaasController < ApplicationController
6
+ def receive
7
+ verifier = SignatureVerifier.new(provider: :asaas, request: request)
8
+ return head :unauthorized unless verifier.valid?
9
+
10
+ payload = JSON.parse(request.raw_post)
11
+ WebhookJob.perform_later(provider: :asaas, payload: payload)
12
+ head :ok
13
+ rescue JSON::ParserError
14
+ head :bad_request
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Payflow
4
+ module Webhooks
5
+ class StripeController < ApplicationController
6
+ def receive
7
+ verifier = SignatureVerifier.new(provider: :stripe, request: request)
8
+ return head :unauthorized unless verifier.valid?
9
+
10
+ payload = JSON.parse(request.raw_post)
11
+ WebhookJob.perform_later(provider: :stripe, payload: payload)
12
+ head :ok
13
+ rescue JSON::ParserError
14
+ head :bad_request
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Payflow
4
+ class ApplicationJob < ActiveJob::Base
5
+ end
6
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Payflow
4
+ class OverdueAccountsJob < ApplicationJob
5
+ queue_as :payflow
6
+
7
+ def perform
8
+ Subscription.active.find_each do |subscription|
9
+ latest_invoice = subscription.invoices.order(created_at: :desc).first
10
+ next unless latest_invoice&.overdue?
11
+
12
+ subscription.update!(status: :overdue)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Payflow
4
+ class WebhookJob < ApplicationJob
5
+ queue_as :payflow
6
+
7
+ def perform(provider:, payload:)
8
+ event = WebhookEvent.create!(
9
+ provider: provider.to_s,
10
+ payload: payload,
11
+ status: :pending
12
+ )
13
+
14
+ Webhooks::Dispatcher.new(webhook_event: event).dispatch!
15
+ event.update!(status: :processed, processed_at: Time.current)
16
+ rescue StandardError
17
+ event&.update!(status: :failed)
18
+ raise
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Payflow
4
+ class ApplicationRecord < ActiveRecord::Base
5
+ self.abstract_class = true
6
+ end
7
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Payflow
4
+ class Invoice < ApplicationRecord
5
+ self.table_name = "payflow_invoices"
6
+
7
+ belongs_to :subscription, class_name: "Payflow::Subscription"
8
+
9
+ enum :status, {
10
+ pending: "pending",
11
+ paid: "paid",
12
+ overdue: "overdue",
13
+ cancelled: "cancelled"
14
+ }
15
+
16
+ validates :external_id, presence: true
17
+ end
18
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Payflow
4
+ class Subscription < ApplicationRecord
5
+ self.table_name = "payflow_subscriptions"
6
+
7
+ belongs_to :billable, polymorphic: true
8
+ has_many :invoices, class_name: "Payflow::Invoice", dependent: :destroy
9
+
10
+ enum :status, {
11
+ pending: "pending",
12
+ active: "active",
13
+ overdue: "overdue",
14
+ cancelled: "cancelled",
15
+ expired: "expired"
16
+ }
17
+
18
+ validates :plan, :provider, :external_id, presence: true
19
+
20
+ def active?
21
+ status == "active"
22
+ end
23
+
24
+ def overdue?
25
+ status == "overdue"
26
+ end
27
+
28
+ def cancelled?
29
+ status == "cancelled"
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Payflow
4
+ class WebhookEvent < ApplicationRecord
5
+ self.table_name = "payflow_webhook_events"
6
+
7
+ enum :status, {
8
+ pending: "pending",
9
+ processed: "processed",
10
+ failed: "failed"
11
+ }
12
+
13
+ validates :provider, :payload, presence: true
14
+
15
+ def processed?
16
+ status == "processed"
17
+ end
18
+ end
19
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ Payflow::Engine.routes.draw do
4
+ namespace :webhooks do
5
+ post "/asaas", to: "asaas#receive"
6
+ post "/stripe", to: "stripe#receive"
7
+ end
8
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreatePayflowSubscriptions < ActiveRecord::Migration[7.0]
4
+ def change
5
+ create_table :payflow_subscriptions do |t|
6
+ t.references :billable, polymorphic: true, null: false, index: true
7
+ t.string :plan, null: false
8
+ t.string :provider, null: false
9
+ t.string :external_id, null: false
10
+ t.string :status, null: false, default: "pending"
11
+ t.datetime :cancelled_at
12
+
13
+ t.timestamps
14
+ end
15
+
16
+ add_index :payflow_subscriptions, :external_id, unique: true
17
+ add_index :payflow_subscriptions, :status
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreatePayflowInvoices < ActiveRecord::Migration[7.0]
4
+ def change
5
+ create_table :payflow_invoices do |t|
6
+ t.references :subscription, null: false, foreign_key: { to_table: :payflow_subscriptions }
7
+ t.string :external_id, null: false
8
+ t.string :status, null: false, default: "pending"
9
+ t.decimal :amount, precision: 10, scale: 2
10
+ t.datetime :due_date
11
+
12
+ t.timestamps
13
+ end
14
+
15
+ add_index :payflow_invoices, :external_id, unique: true
16
+ add_index :payflow_invoices, :status
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreatePayflowWebhookEvents < ActiveRecord::Migration[7.0]
4
+ def change
5
+ create_table :payflow_webhook_events do |t|
6
+ t.string :provider, null: false
7
+ t.json :payload, null: false, default: {}
8
+ t.string :status, null: false, default: "pending"
9
+ t.datetime :processed_at
10
+
11
+ t.timestamps
12
+ end
13
+
14
+ add_index :payflow_webhook_events, :status
15
+ add_index :payflow_webhook_events, :provider
16
+ end
17
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "rails/generators/migration"
5
+
6
+ module Payflow
7
+ module Generators
8
+ class InstallGenerator < Rails::Generators::Base
9
+ include Rails::Generators::Migration
10
+
11
+ source_root File.expand_path("templates", __dir__)
12
+
13
+ desc "Creates Payflow initializer and database migrations"
14
+
15
+ def create_initializer
16
+ template "initializer.rb", "config/initializers/payflow.rb"
17
+ end
18
+
19
+ def create_migrations
20
+ migration_template "migration_subscription.rb", "db/migrate/create_payflow_subscriptions.rb"
21
+ migration_template "migration_invoice.rb", "db/migrate/create_payflow_invoices.rb"
22
+ migration_template "migration_webhook_event.rb", "db/migrate/create_payflow_webhook_events.rb"
23
+ end
24
+
25
+ def show_readme
26
+ return unless behavior == :invoke
27
+
28
+ say "\nPayflow installed successfully!", :green
29
+ say " Run `rails db:migrate` to apply the migrations."
30
+ say ""
31
+ say "Note: Payflow::Engine also appends engine migrations when the gem is loaded."
32
+ say "Use either this generator OR `rails payflow:install:migrations` — not both."
33
+ end
34
+
35
+ def self.next_migration_number(_dirname)
36
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ Payflow.configure do |config|
4
+ config.default_provider = :asaas
5
+
6
+ config.asaas_api_key = ENV.fetch("ASAAS_API_KEY", nil)
7
+ config.asaas_webhook_token = ENV.fetch("ASAAS_WEBHOOK_TOKEN", nil)
8
+
9
+ config.stripe_api_key = ENV.fetch("STRIPE_API_KEY", nil)
10
+ config.stripe_webhook_secret = ENV.fetch("STRIPE_WEBHOOK_SECRET", nil)
11
+
12
+ # config.billable_class_name = "Organization"
13
+ end
@@ -0,0 +1,16 @@
1
+ class CreatePayflowInvoices < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :payflow_invoices do |t|
4
+ t.references :subscription, null: false, foreign_key: { to_table: :payflow_subscriptions }
5
+ t.string :external_id, null: false
6
+ t.string :status, null: false, default: "pending"
7
+ t.decimal :amount, precision: 10, scale: 2
8
+ t.datetime :due_date
9
+
10
+ t.timestamps
11
+ end
12
+
13
+ add_index :payflow_invoices, :external_id, unique: true
14
+ add_index :payflow_invoices, :status
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ class CreatePayflowSubscriptions < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :payflow_subscriptions do |t|
4
+ t.references :billable, polymorphic: true, null: false, index: true
5
+ t.string :plan, null: false
6
+ t.string :provider, null: false
7
+ t.string :external_id, null: false
8
+ t.string :status, null: false, default: "pending"
9
+ t.datetime :cancelled_at
10
+
11
+ t.timestamps
12
+ end
13
+
14
+ add_index :payflow_subscriptions, :external_id, unique: true
15
+ add_index :payflow_subscriptions, :status
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ class CreatePayflowWebhookEvents < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :payflow_webhook_events do |t|
4
+ t.string :provider, null: false
5
+ t.json :payload, null: false, default: {}
6
+ t.string :status, null: false, default: "pending"
7
+ t.datetime :processed_at
8
+
9
+ t.timestamps
10
+ end
11
+
12
+ add_index :payflow_webhook_events, :status
13
+ add_index :payflow_webhook_events, :provider
14
+ end
15
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Payflow
4
+ module Billable
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ has_many :payflow_subscriptions,
9
+ as: :billable,
10
+ class_name: "Payflow::Subscription",
11
+ dependent: :destroy
12
+ end
13
+
14
+ def subscribe!(plan:, provider: Payflow.config.provider)
15
+ SubscriptionService.new(billable: self).subscribe!(plan: plan, provider: provider)
16
+ end
17
+
18
+ def cancel_subscription!
19
+ SubscriptionService.new(billable: self).cancel!
20
+ end
21
+
22
+ def subscription
23
+ payflow_subscriptions.order(created_at: :desc).first
24
+ end
25
+
26
+ def active_subscription?
27
+ subscription&.active?
28
+ end
29
+
30
+ def can_access_system?
31
+ active_subscription? && !subscription&.overdue?
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Payflow
4
+ class Configuration
5
+ attr_accessor :provider,
6
+ :asaas_api_key,
7
+ :asaas_webhook_token,
8
+ :stripe_api_key,
9
+ :stripe_webhook_secret,
10
+ :billable_class_name
11
+
12
+ alias default_provider provider
13
+ alias default_provider= provider=
14
+
15
+ def initialize
16
+ @provider = :asaas
17
+ @billable_class_name = nil
18
+ end
19
+
20
+ def provider_credentials(provider_name)
21
+ case provider_name.to_sym
22
+ when :asaas
23
+ { api_key: asaas_api_key, webhook_token: asaas_webhook_token }
24
+ when :stripe
25
+ { api_key: stripe_api_key, webhook_secret: stripe_webhook_secret }
26
+ else
27
+ raise ProviderNotFoundError, "Unknown provider: #{provider_name}"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Payflow
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace Payflow
6
+
7
+ config.generators do |generator|
8
+ generator.test_framework :rspec
9
+ end
10
+
11
+ initializer "payflow.migrations" do |app|
12
+ unless app.root.to_s.match?(root.to_s)
13
+ config.paths["db/migrate"].expanded.each do |path|
14
+ app.config.paths["db/migrate"] << path
15
+ end
16
+ end
17
+ end
18
+
19
+ rake_tasks do
20
+ load root.join("lib/tasks/payflow_tasks.rake")
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Payflow
4
+ class Error < StandardError; end
5
+
6
+ class ConfigurationError < Error; end
7
+ class ProviderError < Error; end
8
+ class ProviderNotFoundError < ProviderError; end
9
+ class InvalidWebhookError < Error; end
10
+ class SignatureVerificationError < InvalidWebhookError; end
11
+ class RecordNotFoundError < Error; end
12
+ class SubscriptionError < Error; end
13
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Payflow
4
+ module Events
5
+ SUBSCRIPTION_CREATED = "subscription.created"
6
+ INVOICE_PAID = "invoice.paid"
7
+ SUBSCRIPTION_CANCELED = "subscription.canceled"
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Payflow
4
+ class ProviderResolver
5
+ REGISTRY = {
6
+ asaas: Providers::Asaas::Provider,
7
+ stripe: Providers::Stripe::Provider
8
+ }.freeze
9
+
10
+ def self.for(name)
11
+ key = name.to_sym
12
+ klass = REGISTRY[key]
13
+ raise ProviderNotFoundError, "Unknown provider: #{key}" unless klass
14
+
15
+ klass.new
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Payflow
4
+ module Providers
5
+ module Asaas
6
+ class Customer
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ def create(attrs)
12
+ { id: "cus_asaas_stub_#{SecureRandom.hex(4)}", provider: :asaas, email: attrs[:email], name: attrs[:name] }
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end