Skip to content

[BUG] Paddle Billing Unable to assign multiple subscriptions to a paddle customer due to unique processor_id constraint #1164

@znapfel

Description

@znapfel

Apologies if I'm thinking about this the wrong way or if this is related to Jumpstart instead of Pay. I'm pretty new to Rails, and this issue seems to be related to Pay, but I discovered it in Jumpstart.

Bug Report

Describe the Bug:
When creating a Pay Customer the processor_id must be unique. In Paddle Billing, the customer_id ("ctm_xyz") is used as the processor_id.

In Paddle Billing a customer can only have 1 email, but multiple Subscriptions. However the base Pay Customer has a uniqueness validation on it that prevents Account-based subscriptions, like what's found in Jumpstart, from creating additional Pay Subscriptions after the first one is created.

validates :processor_id, allow_blank: true, uniqueness: {scope: :processor, case_sensitive: true}

Jumpstart Subscriptions

Additionally, even if this uniqueness validator is removed, the sync method in the Paddle Billing subscription.rb used by webhooks to update/activate/cancel subscriptions will only ever find the first Pay Customer (Account in Jumpstart) for a given Paddle Billing Customer, not the Pay Customer by looking at the first row with the processor_id included in the webhook event:

pay_customer = Pay::Customer.find_by(processor: :paddle_billing, processor_id: object.customer_id)

The Pay Customer type and id is stored in the custom_data property of the Paddle Billing customer

Image

With that custom data we should be able to look up the correct Pay Customer with that information instead of the processor_id (Paddle Billing customer_id). So the above pay_customer query should become:

pay_customer = Pay::Customer.find_by(processor: :paddle_billing, owner_id: object.custom_data.account) // account is Jumpstart specific. I assume this would become whatever model the Subscription is attached to

To Reproduce:
The easiest way to reproduce would be to use a Jumpstart account and a Paddle Sandbox account

  1. In Paddle Sanbox, create a Product with a Price
  2. Create a Plan in Jumpstart with that associated Paddle Product id
  3. Create a new Jumpstart user
  4. Subscribe to the Plan with the user's personal account
  5. Create a Team Account
  6. Create a Subscription with that Team Account

You'll see a Processor already taken validation failed on api_record due to the update! calls

Image

Change the update! to update and the user will be able to create a subscription due to update skipping validations. However instead of being in the Team Account, The Personal account will contain both Subscriptions.

Expected Behavior:
This may be a misunderstanding on my part, but I would assume the Paddle Customer could have n active subscriptions where n is the number of Accounts their user has in Jumpstart. For example, a User could be paying for a personal account and 2 team accounts.

Actual Behavior:
A user can create n accounts, but can only have a single subscribed account due to the the uniqueness constraint preventing the processor_id, which is a customer_id associated with the email of the User who owns the n accounts, from being attached to the Pay Customer record associated with an account.

Environment:

  • Pay gem version: 10.0

  • Ruby version: 3.4.4

  • Rails version: 8.0.2

  • Operating System: Ubuntu WSL2

Possible Fix:
Remove the uniqueness validation on the processor_id for Paddle Billing, and attach the Subscription to the correct Pay Customer by using the custom_data property in the webhook object instead of the processor_id which will always return the first instance with that processor_id.

Steps to Reproduce with Fix (if available):

Related Issues:
Pay Gem Issue with Paddle Billing and Multiple Accounts to a Single User

Labels to Apply:
Bug

Checklist:

  • I have searched for similar issues and couldn't find any
  • I have checked the documentation for relevant information
  • I have included all the required information

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions