pay-rails / pay

Payments for Ruby on Rails apps
https://github.com/pay-rails/pay
MIT License
1.98k stars 316 forks source link

Paddle Billing transactions which include credit #874

Closed deanpcmad closed 1 year ago

deanpcmad commented 1 year ago

With Paddle Billing, credits maybe added to a transaction such as if a subscription's plan is upgraded, a credit is added for pro-rated updates.

For example, here's a webhook event for a plan upgrade from $15 to $20

{
  "event_id": "evt_abc123",
  "event_type": "transaction.completed",
  "occurred_at": "2023-10-18T10:56:33.696554Z",
  "notification_id": "ntf_abc123",
  "data": {
    "id": "txn_abc123",
    "items": [
      {
        "price": {
          "id": "pri_abc123",
          "status": "active",
          "quantity": {
            "maximum": 100,
            "minimum": 1
          },
          "tax_mode": "account_setting",
          "product_id": "pro_abc123",
          "unit_price": {
            "amount": "2000",
            "currency_code": "USD"
          },
          "custom_data": null,
          "description": "20 monthly",
          "trial_period": null,
          "billing_cycle": {
            "interval": "month",
            "frequency": 1
          },
          "unit_price_overrides": []
        },
        "price_id": "pri_abc123",
        "quantity": 1,
        "proration": {
          "rate": "0.99966",
          "billing_period": {
            "ends_at": "2023-11-18T10:41:13.871061Z",
            "starts_at": "2023-10-18T10:56:23.015Z"
          }
        }
      }
    ],
    "origin": "subscription_update",
    "status": "completed",
    "details": {
      "totals": {
        "fee": "150",
        "tax": "333",
        "total": "1999",
        "credit": "1500",
        "balance": "0",
        "discount": "0",
        "earnings": "1516",
        "subtotal": "1666",
        "grand_total": "499",
        "currency_code": "USD"
      },
      "line_items": [
        {
          "id": "txnitm_abc123",
          "totals": {
            "tax": "333",
            "total": "1999",
            "discount": "0",
            "subtotal": "1666"
          },
          "product": {
            "id": "pro_abc123",
            "name": "My Test Payments App",
            "status": "active",
            "image_url": null,
            "custom_data": null,
            "description": null,
            "tax_category": "standard"
          },
          "price_id": "pri_abc123",
          "quantity": 1,
          "tax_rate": "0.2",
          "proration": {
            "rate": "0.99966",
            "billing_period": {
              "ends_at": "2023-11-18T10:41:13.871061Z",
              "starts_at": "2023-10-18T10:56:23.015Z"
            }
          },
          "unit_totals": {
            "tax": "333",
            "total": "1999",
            "discount": "0",
            "subtotal": "1666"
          }
        }
      ],
      "payout_totals": {
        "fee": "150",
        "tax": "333",
        "total": "1999",
        "credit": "1500",
        "balance": "0",
        "discount": "0",
        "earnings": "1516",
        "fee_rate": "0.05",
        "subtotal": "1666",
        "grand_total": "499",
        "currency_code": "USD",
        "exchange_rate": "1"
      },
      "tax_rates_used": [
        {
          "totals": {
            "tax": "333",
            "total": "1999",
            "discount": "0",
            "subtotal": "1666"
          },
          "tax_rate": "0.2"
        }
      ],
      "adjusted_totals": {
        "fee": "150",
        "tax": "333",
        "total": "1999",
        "earnings": "1516",
        "subtotal": "1666",
        "grand_total": "499",
        "currency_code": "USD"
      }
    },
    "checkout": {
      "url": "https://localhost:3000/charges?_ptxn=txn_abc123"
    },
    "payments": [
      {
        "amount": "499",
        "status": "captured",
        "created_at": "2023-10-18T10:56:23.651391Z",
        "error_code": null,
        "captured_at": "2023-10-18T10:56:26.068085Z",
        "method_details": {
          "card": {
            "type": "visa",
            "last4": "4242",
            "expiry_year": 2030,
            "expiry_month": 1
          },
          "type": "card"
        },
        "payment_attempt_id": "769c7528-df6b-4ca5-91b6-41d1ee38094d",
        "stored_payment_method_id": "de146e8c-6361-433f-8d15-5b2ee59efd9b"
      }
    ],
    "billed_at": "2023-10-18T10:56:23.434705Z",
    "address_id": "add_abc123",
    "created_at": "2023-10-18T10:56:23.50414Z",
    "invoice_id": "inv_abc123",
    "updated_at": "2023-10-18T10:56:33.115863546Z",
    "business_id": null,
    "custom_data": null,
    "customer_id": "ctm_abc123",
    "discount_id": null,
    "currency_code": "USD",
    "billing_period": {
      "ends_at": "2023-11-18T10:41:13.871061Z",
      "starts_at": "2023-10-18T10:41:13.871061Z"
    },
    "invoice_number": "3731-10089",
    "billing_details": null,
    "collection_mode": "automatic",
    "subscription_id": "sub_abc123"
  }
}

You can see there's a credit value in details.totals. Should the Paddle Charge code be updated so it creates charges for the payments amount? Or should it create the credits as refunds? I'm unsure if Stripe does the same or supports credits.

excid3 commented 1 year ago

We should be using "grand_total": "499" then, not total.

Also... why can't they have a better name for this? 🙃