pay 7.3.0 → 8.0.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 +4 -4
 - data/README.md +2 -0
 - data/app/controllers/pay/webhooks/lemon_squeezy_controller.rb +45 -0
 - data/app/jobs/pay/customer_sync_job.rb +1 -1
 - data/app/models/concerns/pay/routing.rb +13 -0
 - data/{lib → app/models}/pay/braintree/charge.rb +5 -12
 - data/{lib/pay/braintree/billable.rb → app/models/pay/braintree/customer.rb} +31 -71
 - data/{lib → app/models}/pay/braintree/payment_method.rb +1 -9
 - data/{lib → app/models}/pay/braintree/subscription.rb +14 -52
 - data/app/models/pay/charge.rb +8 -27
 - data/app/models/pay/customer.rb +2 -15
 - data/app/models/pay/fake_processor/charge.rb +13 -0
 - data/{lib/pay/fake_processor/billable.rb → app/models/pay/fake_processor/customer.rb} +20 -37
 - data/{lib → app/models}/pay/fake_processor/merchant.rb +2 -9
 - data/app/models/pay/fake_processor/payment_method.rb +11 -0
 - data/app/models/pay/fake_processor/subscription.rb +60 -0
 - data/app/models/pay/lemon_squeezy/charge.rb +86 -0
 - data/app/models/pay/lemon_squeezy/customer.rb +78 -0
 - data/app/models/pay/lemon_squeezy/payment_method.rb +27 -0
 - data/app/models/pay/lemon_squeezy/subscription.rb +129 -0
 - data/app/models/pay/merchant.rb +0 -11
 - data/{lib → app/models}/pay/paddle_billing/charge.rb +2 -8
 - data/{lib/pay/paddle_billing/billable.rb → app/models/pay/paddle_billing/customer.rb} +18 -35
 - data/{lib → app/models}/pay/paddle_billing/payment_method.rb +2 -12
 - data/{lib → app/models}/pay/paddle_billing/subscription.rb +9 -33
 - data/{lib → app/models}/pay/paddle_classic/charge.rb +13 -18
 - data/{lib/pay/paddle_classic/billable.rb → app/models/pay/paddle_classic/customer.rb} +9 -31
 - data/{lib → app/models}/pay/paddle_classic/payment_method.rb +1 -11
 - data/{lib → app/models}/pay/paddle_classic/subscription.rb +11 -36
 - data/app/models/pay/payment_method.rb +0 -5
 - data/{lib → app/models}/pay/stripe/charge.rb +6 -22
 - data/{lib/pay/stripe/billable.rb → app/models/pay/stripe/customer.rb} +73 -108
 - data/{lib → app/models}/pay/stripe/merchant.rb +2 -11
 - data/{lib → app/models}/pay/stripe/payment_method.rb +2 -10
 - data/{lib → app/models}/pay/stripe/subscription.rb +37 -71
 - data/app/models/pay/subscription.rb +7 -37
 - data/app/models/pay/webhook.rb +2 -0
 - data/config/routes.rb +1 -0
 - data/db/migrate/2_add_pay_sti_columns.rb +24 -0
 - data/lib/pay/attributes.rb +11 -3
 - data/lib/pay/braintree.rb +25 -6
 - data/lib/pay/engine.rb +2 -0
 - data/lib/pay/fake_processor.rb +2 -6
 - data/lib/pay/lemon_squeezy/webhooks/order.rb +11 -0
 - data/lib/pay/lemon_squeezy/webhooks/subscription.rb +3 -3
 - data/lib/pay/lemon_squeezy/webhooks/subscription_payment.rb +11 -0
 - data/lib/pay/lemon_squeezy.rb +56 -104
 - data/lib/pay/paddle_billing.rb +15 -6
 - data/lib/pay/paddle_classic.rb +11 -9
 - data/lib/pay/receipts.rb +6 -6
 - data/lib/pay/stripe/webhooks/customer_updated.rb +1 -1
 - data/lib/pay/stripe.rb +15 -6
 - data/lib/pay/version.rb +1 -1
 - data/lib/pay.rb +12 -1
 - metadata +34 -38
 - data/app/views/pay/stripe/_checkout_button.html.erb +0 -21
 - data/lib/pay/braintree/authorization_error.rb +0 -9
 - data/lib/pay/braintree/error.rb +0 -23
 - data/lib/pay/fake_processor/charge.rb +0 -21
 - data/lib/pay/fake_processor/error.rb +0 -6
 - data/lib/pay/fake_processor/payment_method.rb +0 -21
 - data/lib/pay/fake_processor/subscription.rb +0 -90
 - data/lib/pay/lemon_squeezy/billable.rb +0 -90
 - data/lib/pay/lemon_squeezy/charge.rb +0 -68
 - data/lib/pay/lemon_squeezy/error.rb +0 -7
 - data/lib/pay/lemon_squeezy/payment_method.rb +0 -40
 - data/lib/pay/lemon_squeezy/subscription.rb +0 -185
 - data/lib/pay/lemon_squeezy/webhooks/transaction_completed.rb +0 -11
 - data/lib/pay/paddle_billing/error.rb +0 -7
 - data/lib/pay/paddle_classic/error.rb +0 -7
 - data/lib/pay/stripe/error.rb +0 -7
 
| 
         @@ -1,185 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            module Pay
         
     | 
| 
       2 
     | 
    
         
            -
              module PaddleBilling
         
     | 
| 
       3 
     | 
    
         
            -
                class Subscription
         
     | 
| 
       4 
     | 
    
         
            -
                  attr_reader :pay_subscription
         
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
     | 
    
         
            -
                  delegate :active?,
         
     | 
| 
       7 
     | 
    
         
            -
                    :canceled?,
         
     | 
| 
       8 
     | 
    
         
            -
                    :on_grace_period?,
         
     | 
| 
       9 
     | 
    
         
            -
                    :on_trial?,
         
     | 
| 
       10 
     | 
    
         
            -
                    :ends_at,
         
     | 
| 
       11 
     | 
    
         
            -
                    :name,
         
     | 
| 
       12 
     | 
    
         
            -
                    :owner,
         
     | 
| 
       13 
     | 
    
         
            -
                    :pause_starts_at,
         
     | 
| 
       14 
     | 
    
         
            -
                    :pause_starts_at?,
         
     | 
| 
       15 
     | 
    
         
            -
                    :processor_id,
         
     | 
| 
       16 
     | 
    
         
            -
                    :processor_plan,
         
     | 
| 
       17 
     | 
    
         
            -
                    :processor_subscription,
         
     | 
| 
       18 
     | 
    
         
            -
                    :prorate,
         
     | 
| 
       19 
     | 
    
         
            -
                    :prorate?,
         
     | 
| 
       20 
     | 
    
         
            -
                    :quantity,
         
     | 
| 
       21 
     | 
    
         
            -
                    :quantity?,
         
     | 
| 
       22 
     | 
    
         
            -
                    :trial_ends_at,
         
     | 
| 
       23 
     | 
    
         
            -
                    to: :pay_subscription
         
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
                  def self.sync_from_transaction(transaction_id)
         
     | 
| 
       26 
     | 
    
         
            -
                    transaction = ::Paddle::Transaction.retrieve(id: transaction_id)
         
     | 
| 
       27 
     | 
    
         
            -
                    sync(transaction.subscription_id) if transaction.subscription_id
         
     | 
| 
       28 
     | 
    
         
            -
                  end
         
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
     | 
    
         
            -
                  def self.sync(subscription_id, object: nil, name: Pay.default_product_name)
         
     | 
| 
       31 
     | 
    
         
            -
                    # Passthrough is not return from this API, so we can't use that
         
     | 
| 
       32 
     | 
    
         
            -
                    object ||= ::Paddle::Subscription.retrieve(id: subscription_id)
         
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
                    pay_customer = Pay::Customer.find_by(processor: :paddle_billing, processor_id: object.customer_id)
         
     | 
| 
       35 
     | 
    
         
            -
                    return unless pay_customer
         
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
                    attributes = {
         
     | 
| 
       38 
     | 
    
         
            -
                      current_period_end: object.current_billing_period&.ends_at,
         
     | 
| 
       39 
     | 
    
         
            -
                      current_period_start: object.current_billing_period&.starts_at,
         
     | 
| 
       40 
     | 
    
         
            -
                      ends_at: (object.canceled_at ? Time.parse(object.canceled_at) : nil),
         
     | 
| 
       41 
     | 
    
         
            -
                      metadata: object.custom_data,
         
     | 
| 
       42 
     | 
    
         
            -
                      paddle_cancel_url: object.management_urls&.cancel,
         
     | 
| 
       43 
     | 
    
         
            -
                      paddle_update_url: object.management_urls&.update_payment_method,
         
     | 
| 
       44 
     | 
    
         
            -
                      pause_starts_at: (object.paused_at ? Time.parse(object.paused_at) : nil),
         
     | 
| 
       45 
     | 
    
         
            -
                      status: object.status
         
     | 
| 
       46 
     | 
    
         
            -
                    }
         
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
                    if object.items&.first
         
     | 
| 
       49 
     | 
    
         
            -
                      item = object.items.first
         
     | 
| 
       50 
     | 
    
         
            -
                      attributes[:processor_plan] = item.price.id
         
     | 
| 
       51 
     | 
    
         
            -
                      attributes[:quantity] = item.quantity
         
     | 
| 
       52 
     | 
    
         
            -
                    end
         
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
                    case attributes[:status]
         
     | 
| 
       55 
     | 
    
         
            -
                    when "canceled"
         
     | 
| 
       56 
     | 
    
         
            -
                      # Remove payment methods since customer cannot be reused after cancelling
         
     | 
| 
       57 
     | 
    
         
            -
                      Pay::PaymentMethod.where(customer_id: object.customer_id).destroy_all
         
     | 
| 
       58 
     | 
    
         
            -
                    when "trialing"
         
     | 
| 
       59 
     | 
    
         
            -
                      attributes[:trial_ends_at] = Time.parse(object.next_billed_at)
         
     | 
| 
       60 
     | 
    
         
            -
                    when "paused"
         
     | 
| 
       61 
     | 
    
         
            -
                      attributes[:pause_starts_at] = Time.parse(object.paused_at)
         
     | 
| 
       62 
     | 
    
         
            -
                    end
         
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
                    case object.scheduled_change&.action
         
     | 
| 
       65 
     | 
    
         
            -
                    when "cancel"
         
     | 
| 
       66 
     | 
    
         
            -
                      attributes[:ends_at] = Time.parse(object.scheduled_change.effective_at)
         
     | 
| 
       67 
     | 
    
         
            -
                    when "pause"
         
     | 
| 
       68 
     | 
    
         
            -
                      attributes[:pause_starts_at] = Time.parse(object.scheduled_change.effective_at)
         
     | 
| 
       69 
     | 
    
         
            -
                    when "resume"
         
     | 
| 
       70 
     | 
    
         
            -
                      attributes[:pause_resumes_at] = Time.parse(object.scheduled_change.effective_at)
         
     | 
| 
       71 
     | 
    
         
            -
                    end
         
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
                    # Update or create the subscription
         
     | 
| 
       74 
     | 
    
         
            -
                    if (pay_subscription = pay_customer.subscriptions.find_by(processor_id: subscription_id))
         
     | 
| 
       75 
     | 
    
         
            -
                      pay_subscription.with_lock do
         
     | 
| 
       76 
     | 
    
         
            -
                        pay_subscription.update!(attributes)
         
     | 
| 
       77 
     | 
    
         
            -
                      end
         
     | 
| 
       78 
     | 
    
         
            -
                      pay_subscription
         
     | 
| 
       79 
     | 
    
         
            -
                    else
         
     | 
| 
       80 
     | 
    
         
            -
                      pay_customer.subscriptions.create!(attributes.merge(name: name, processor_id: subscription_id))
         
     | 
| 
       81 
     | 
    
         
            -
                    end
         
     | 
| 
       82 
     | 
    
         
            -
                  end
         
     | 
| 
       83 
     | 
    
         
            -
             
     | 
| 
       84 
     | 
    
         
            -
                  def initialize(pay_subscription)
         
     | 
| 
       85 
     | 
    
         
            -
                    @pay_subscription = pay_subscription
         
     | 
| 
       86 
     | 
    
         
            -
                  end
         
     | 
| 
       87 
     | 
    
         
            -
             
     | 
| 
       88 
     | 
    
         
            -
                  def subscription(**options)
         
     | 
| 
       89 
     | 
    
         
            -
                    @paddle_billing_subscription ||= ::Paddle::Subscription.retrieve(id: processor_id, **options)
         
     | 
| 
       90 
     | 
    
         
            -
                  end
         
     | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
       92 
     | 
    
         
            -
                  # Get a transaction to update payment method
         
     | 
| 
       93 
     | 
    
         
            -
                  def payment_method_transaction
         
     | 
| 
       94 
     | 
    
         
            -
                    ::Paddle::Subscription.get_transaction(id: processor_id)
         
     | 
| 
       95 
     | 
    
         
            -
                  end
         
     | 
| 
       96 
     | 
    
         
            -
             
     | 
| 
       97 
     | 
    
         
            -
                  # If a subscription is paused, cancel immediately
         
     | 
| 
       98 
     | 
    
         
            -
                  # Otherwise, cancel at period end
         
     | 
| 
       99 
     | 
    
         
            -
                  def cancel(**options)
         
     | 
| 
       100 
     | 
    
         
            -
                    return if canceled?
         
     | 
| 
       101 
     | 
    
         
            -
             
     | 
| 
       102 
     | 
    
         
            -
                    response = ::Paddle::Subscription.cancel(
         
     | 
| 
       103 
     | 
    
         
            -
                      id: processor_id,
         
     | 
| 
       104 
     | 
    
         
            -
                      effective_from: options.fetch(:effective_from, (paused? ? "immediately" : "next_billing_period"))
         
     | 
| 
       105 
     | 
    
         
            -
                    )
         
     | 
| 
       106 
     | 
    
         
            -
                    pay_subscription.update(
         
     | 
| 
       107 
     | 
    
         
            -
                      status: response.status,
         
     | 
| 
       108 
     | 
    
         
            -
                      ends_at: response.scheduled_change.effective_at
         
     | 
| 
       109 
     | 
    
         
            -
                    )
         
     | 
| 
       110 
     | 
    
         
            -
                  rescue ::Paddle::Error => e
         
     | 
| 
       111 
     | 
    
         
            -
                    raise Pay::PaddleBilling::Error, e
         
     | 
| 
       112 
     | 
    
         
            -
                  end
         
     | 
| 
       113 
     | 
    
         
            -
             
     | 
| 
       114 
     | 
    
         
            -
                  def cancel_now!(**options)
         
     | 
| 
       115 
     | 
    
         
            -
                    cancel(options.merge(effective_from: "immediately"))
         
     | 
| 
       116 
     | 
    
         
            -
                  rescue ::Paddle::Error => e
         
     | 
| 
       117 
     | 
    
         
            -
                    raise Pay::PaddleBilling::Error, e
         
     | 
| 
       118 
     | 
    
         
            -
                  end
         
     | 
| 
       119 
     | 
    
         
            -
             
     | 
| 
       120 
     | 
    
         
            -
                  def change_quantity(quantity, **options)
         
     | 
| 
       121 
     | 
    
         
            -
                    items = [{
         
     | 
| 
       122 
     | 
    
         
            -
                      price_id: processor_plan,
         
     | 
| 
       123 
     | 
    
         
            -
                      quantity: quantity
         
     | 
| 
       124 
     | 
    
         
            -
                    }]
         
     | 
| 
       125 
     | 
    
         
            -
             
     | 
| 
       126 
     | 
    
         
            -
                    ::Paddle::Subscription.update(id: processor_id, items: items, proration_billing_mode: "prorated_immediately")
         
     | 
| 
       127 
     | 
    
         
            -
                  rescue ::Paddle::Error => e
         
     | 
| 
       128 
     | 
    
         
            -
                    raise Pay::PaddleBilling::Error, e
         
     | 
| 
       129 
     | 
    
         
            -
                  end
         
     | 
| 
       130 
     | 
    
         
            -
             
     | 
| 
       131 
     | 
    
         
            -
                  # A subscription could be set to cancel or pause in the future
         
     | 
| 
       132 
     | 
    
         
            -
                  # It is considered on grace period until the cancel or pause time begins
         
     | 
| 
       133 
     | 
    
         
            -
                  def on_grace_period?
         
     | 
| 
       134 
     | 
    
         
            -
                    (canceled? && Time.current < ends_at) || (paused? && pause_starts_at? && Time.current < pause_starts_at)
         
     | 
| 
       135 
     | 
    
         
            -
                  end
         
     | 
| 
       136 
     | 
    
         
            -
             
     | 
| 
       137 
     | 
    
         
            -
                  def paused?
         
     | 
| 
       138 
     | 
    
         
            -
                    pay_subscription.status == "paused"
         
     | 
| 
       139 
     | 
    
         
            -
                  end
         
     | 
| 
       140 
     | 
    
         
            -
             
     | 
| 
       141 
     | 
    
         
            -
                  def pause
         
     | 
| 
       142 
     | 
    
         
            -
                    response = ::Paddle::Subscription.pause(id: processor_id)
         
     | 
| 
       143 
     | 
    
         
            -
                    pay_subscription.update!(status: :paused, pause_starts_at: response.scheduled_change.effective_at)
         
     | 
| 
       144 
     | 
    
         
            -
                  rescue ::Paddle::Error => e
         
     | 
| 
       145 
     | 
    
         
            -
                    raise Pay::PaddleBilling::Error, e
         
     | 
| 
       146 
     | 
    
         
            -
                  end
         
     | 
| 
       147 
     | 
    
         
            -
             
     | 
| 
       148 
     | 
    
         
            -
                  def resumable?
         
     | 
| 
       149 
     | 
    
         
            -
                    paused?
         
     | 
| 
       150 
     | 
    
         
            -
                  end
         
     | 
| 
       151 
     | 
    
         
            -
             
     | 
| 
       152 
     | 
    
         
            -
                  def resume
         
     | 
| 
       153 
     | 
    
         
            -
                    unless resumable?
         
     | 
| 
       154 
     | 
    
         
            -
                      raise StandardError, "You can only resume paused subscriptions."
         
     | 
| 
       155 
     | 
    
         
            -
                    end
         
     | 
| 
       156 
     | 
    
         
            -
             
     | 
| 
       157 
     | 
    
         
            -
                    # Paddle Billing API only allows "resuming" subscriptions when they are paused
         
     | 
| 
       158 
     | 
    
         
            -
                    # So cancel the scheduled change if it is in the future
         
     | 
| 
       159 
     | 
    
         
            -
                    if paused? && pause_starts_at? && Time.current < pause_starts_at
         
     | 
| 
       160 
     | 
    
         
            -
                      ::Paddle::Subscription.update(id: processor_id, scheduled_change: nil)
         
     | 
| 
       161 
     | 
    
         
            -
                    else
         
     | 
| 
       162 
     | 
    
         
            -
                      ::Paddle::Subscription.resume(id: processor_id, effective_from: "immediately")
         
     | 
| 
       163 
     | 
    
         
            -
                    end
         
     | 
| 
       164 
     | 
    
         
            -
             
     | 
| 
       165 
     | 
    
         
            -
                    pay_subscription.update(status: :active, pause_starts_at: nil)
         
     | 
| 
       166 
     | 
    
         
            -
                  rescue ::Paddle::Error => e
         
     | 
| 
       167 
     | 
    
         
            -
                    raise Pay::PaddleBilling::Error, e
         
     | 
| 
       168 
     | 
    
         
            -
                  end
         
     | 
| 
       169 
     | 
    
         
            -
             
     | 
| 
       170 
     | 
    
         
            -
                  def swap(plan, **options)
         
     | 
| 
       171 
     | 
    
         
            -
                    items = [{
         
     | 
| 
       172 
     | 
    
         
            -
                      price_id: plan,
         
     | 
| 
       173 
     | 
    
         
            -
                      quantity: quantity || 1
         
     | 
| 
       174 
     | 
    
         
            -
                    }]
         
     | 
| 
       175 
     | 
    
         
            -
             
     | 
| 
       176 
     | 
    
         
            -
                    ::Paddle::Subscription.update(id: processor_id, items: items, proration_billing_mode: "prorated_immediately")
         
     | 
| 
       177 
     | 
    
         
            -
                    pay_subscription.update(processor_plan: plan, ends_at: nil, status: :active)
         
     | 
| 
       178 
     | 
    
         
            -
                  end
         
     | 
| 
       179 
     | 
    
         
            -
             
     | 
| 
       180 
     | 
    
         
            -
                  # Retries the latest invoice for a Past Due subscription
         
     | 
| 
       181 
     | 
    
         
            -
                  def retry_failed_payment
         
     | 
| 
       182 
     | 
    
         
            -
                  end
         
     | 
| 
       183 
     | 
    
         
            -
                end
         
     | 
| 
       184 
     | 
    
         
            -
              end
         
     | 
| 
       185 
     | 
    
         
            -
            end
         
     |