Esta PoC valida que company puede usar Chargebee como billing engine, recibir eventos por webhook, ejecutar el pago por fuera de Chargebee y luego marcar la invoice como pagada con record_payment.
Chargebee crea subscription
→ Chargebee genera invoice payment_due
→ Chargebee envía webhook al backend de company
→ company ejecutaría el pago crypto/onchain
→ company llama record_payment
→ Chargebee marca invoice paid
→ Chargebee envía invoice_updated / payment_succeededNecesitás:
- Cuenta sandbox/test de Chargebee.
- Full-Access API Key de Chargebee.
- Node.js instalado.
- curl instalado.
- WSL/Linux terminal.
- cloudflared para exponer el webhook local.
No hace falta npm install para estos scripts.
Al crear la cuenta/test site usamos:
What are you looking to monetize? Software/Apps
Number of employees: 1-9
Host my data in: US Data CenterPara conseguir la API key:
Settings
→ Configure Chargebee
→ API Keys and Webhooks
→ API Keys
→ usar Full-Access KeyNo usar la publishable key. Para estos scripts se usa la Full-Access Key.
Desde la carpeta del proyecto:
cd ~/undr/yummy/PoC/chargebeeCrear o editar .env:
nano .envContenido base:
CHARGEBEE_SITE="comunyt-test"
CHARGEBEE_API_KEY="TU_FULL_ACCESS_KEY"
MERCHANT_ID="merchant-demo-001"
ITEM_FAMILY_ID="comunyt-family"
ITEM_FAMILY_NAME="Comunyt Demo Family"
ITEM_ID="comunyt-demo-plan"
ITEM_NAME="comunyt Demo Subscription"
ITEM_PRICE_ID="comunyt-demo-plan-usd-monthly"
ITEM_PRICE_NAME="comunyt Demo Plan USD Monthly"
ITEM_PRICE_AMOUNT="1000"
ITEM_PRICE_CURRENCY="USD"
CUSTOMER_ID="comunyt-user-demo-001"
CUSTOMER_EMAIL="demo-user@comunyt.test"
CUSTOMER_FIRST_NAME="Demo"
CUSTOMER_LAST_NAME="User"
INVOICE_ID=""
SUBSCRIPTION_ID=""Notas:
CHARGEBEE_SITEes el subdominio de Chargebee. Si tu URL eshttps://comunyt-test.chargebee.com, entonces el site escomunyt-test.ITEM_PRICE_AMOUNT="1000"significa 10.00 USD porque Chargebee usa cents.auto_collection=offse usa para que Chargebee no intente cobrar con tarjeta/gateway. company registra pagos externos.
chmod +x scripts/*.sh./scripts/00_check_auth.shVerificar:
cat data/00_auth_check.pretty.jsonResultado esperado:
No debe aparecer api_error_code.
Debe devolver una respuesta válida de Chargebee.Chargebee Product Catalog 2.0 requiere crear primero una Item Family.
./scripts/00b_create_item_family.shVerificar:
cat data/00b_create_item_family.pretty.jsonResultado esperado:
item_family.id = comunyt-family
item_family.status = active./scripts/01_create_item.shVerificar:
cat data/01_create_item.pretty.jsonResultado esperado:
item.id = comunyt-demo-plan
item.type = plan
item.item_family_id = comunyt-family
item.status = active./scripts/02_create_item_price.shVerificar:
cat data/02_create_item_price.pretty.jsonResultado esperado:
item_price.id = comunyt-demo-plan-usd-monthly
price = 1000
period = 1
period_unit = month
currency_code = USD
status = activeEsto valida que company puede crear catálogo y pricing recurrente vía API.
./scripts/03_create_customer.shVerificar:
cat data/03_create_customer.pretty.jsonResultado esperado:
customer.id = comunyt-user-demo-001
auto_collection = off
card_status = no_cardEsto es clave: el customer existe sin payment method tradicional.
./scripts/04_create_subscription.shVerificar:
cat data/04_create_subscription.pretty.jsonResultado esperado:
subscription.status = active
subscription.auto_collection = off
invoice.status = payment_due
invoice.recurring = true
invoice.amount_due = 1000Copiar estos valores desde el JSON:
subscription.id
invoice.idEditar .env:
nano .envAgregar por ejemplo:
SUBSCRIPTION_ID="169m1FVKMxCde1EyX"
INVOICE_ID="1"Usar los IDs reales que devuelva tu ejecución.
./scripts/06_retrieve_invoice.sh && cat data/06_retrieve_invoice.pretty.jsonResultado esperado antes del pago:
invoice.status = payment_due
amount_due = 1000
amount_paid = 0Este paso simula lo que haría company después de cobrar crypto/onchain.
./scripts/07_record_payment.shVerificar:
cat data/07_record_payment.pretty.jsonResultado esperado:
invoice.status = paid
amount_due = 0
amount_paid = 1000
transaction.status = success
transaction.gateway = not_applicable
transaction.payment_method = bank_transferEsto valida que Chargebee acepta pagos externos y marca la invoice como pagada.
./scripts/06_retrieve_invoice.sh && cat data/06_retrieve_invoice.pretty.jsonResultado esperado:
status = paid
amount_due = 0
linked_payments existe./scripts/08_retrieve_subscription.sh && cat data/08_retrieve_subscription.pretty.jsonResultado esperado:
subscription.status = active
auto_collection = off
due_invoices_count = 0
next_billing_at existe
mrr = 1000Esto valida que pagar externamente no rompe el ciclo de billing.
./scripts/05_list_due_invoices.sh && cat data/05_list_due_invoices.pretty.jsonResultado esperado:
list = []Esto confirma que la invoice ya no está como payment_due.
Si no tenés ngrok, usar cloudflared.
Instalar:
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o cloudflared && chmod +x cloudflared && sudo mv cloudflared /usr/local/bin/cloudflaredAbrir una terminal nueva. Terminal 1:
cd ~/undr/yummy/PoC/chargebee && node scripts/09_mock_webhook_receiver.mjsResultado esperado:
Mock webhook receiver listening on http://localhost:8787/chargebee/webhookNo cerrar esta terminal.
Abrir otra terminal nueva. Terminal 2:
cloudflared tunnel --url http://localhost:8787Copiar la URL pública que devuelve, por ejemplo:
https://relying-achieving-transaction-pamela.trycloudflare.comNo cerrar esta terminal.
En Chargebee:
Settings
→ Configure Chargebee
→ API Keys and Webhooks
→ Webhooks
→ Add WebhookCompletar:
Webhook Name: company Local Webhook
Webhook URL: https://TU_URL_TRYCLOUDFLARE.trycloudflare.com/chargebee/webhook
Protect webhook URL with basic authentication: OFF
API version: Version 2
Events to Send: All Events
Set this as primary: OFF
Exclude card information: ONGuardar con Create.
Abrir una tercera terminal. Terminal 3:
cd ~/undr/yummy/PoC/chargebeeCrear otra subscription usando el mismo customer:
./scripts/04_create_subscription.shEsto genera una nueva invoice y debería disparar eventos webhooks.
En Terminal 1 deberías ver eventos recibidos, por ejemplo:
invoice_generated
subscription_createdTambién podés verificar archivos guardados:
ls -t data/webhook-*.json | headVer el último webhook:
ls -t data/webhook-*.json | head -1 | xargs catResultado esperado:
event_type = invoice_generated
invoice.status = payment_due
invoice.id existe
subscription.id existeDel webhook o del 04_create_subscription.pretty.json, copiar:
invoice.id
subscription.idEditar .env:
nano .envActualizar:
INVOICE_ID="NUEVO_INVOICE_ID"
SUBSCRIPTION_ID="NUEVO_SUBSCRIPTION_ID"Registrar pago externo:
./scripts/07_record_payment.shVerificar:
./scripts/06_retrieve_invoice.sh && cat data/06_retrieve_invoice.pretty.jsonResultado esperado:
invoice.status = paid
amount_due = 0
amount_paid = 1000En Terminal 1 deberían llegar nuevos webhooks:
invoice_updated
payment_succeededEsto valida el loop completo:
Chargebee genera invoice
→ webhook llega al backend mock
→ company registra pago externo
→ Chargebee marca invoice paid
→ Chargebee envía payment_succeededSolo hacer esto cuando ya no quieras seguir usando esa subscription.
Verificar que .env tenga el SUBSCRIPTION_ID correcto.
Importante:
Para Product Catalog 2.0 el script debe usar:
/subscriptions/{SUBSCRIPTION_ID}/cancel_for_itemsNo usar el endpoint legacy de cancelación o Chargebee devolverá:
configuration_incompatible
pc2_to_pc1_errorEjecutar:
./scripts/10_cancel_subscription.sh && cat data/10_cancel_subscription.pretty.jsonResultado esperado:
subscription.status = cancelledTambién debería llegar webhook de cancelación al receiver si el webhook sigue activo.
Esta PoC valida:
1. company puede crear catálogo recurrente en Chargebee vía API.
2. company puede crear customers sin tarjeta.
3. company puede crear subscriptions con auto_collection=off.
4. Chargebee genera invoices recurring payment_due.
5. company puede registrar pagos externos con record_payment.
6. Chargebee marca invoices como paid.
7. Chargebee mantiene subscription active y next_billing_at.
8. Chargebee envía webhooks reales al backend local.
9. El backend puede recibir invoice_generated, subscription_created, invoice_updated, payment_succeeded y subscription_cancelled.
10. El modelo Chargebee billing engine + company external crypto payment rail es viable.Solución:
Crear primero Item Family con 00b_create_item_family.sh y pasar item_family_id al crear item.
Solución:
Usar endpoint PC 2.0:
/customers/{CUSTOMER_ID}/subscription_for_itemsNo usar endpoint legacy de subscriptions.
No se usó para la PoC final. El endpoint de one-time invoice requiere charge item price, no plan item price. Para validar webhooks usamos otra subscription, que es más cercana al flujo real.
Error:
configuration_incompatible
pc2_to_pc1_errorSolución:
Usar endpoint PC 2.0:
/subscriptions/{SUBSCRIPTION_ID}/cancel_for_itemsNo usar el endpoint legacy de cancelación.
No corresponde. Los scripts son bash/curl y el receiver usa Node nativo.
Se usó cloudflared tunnel --url http://localhost:8787.
El flujo validado para company es:
Chargebee = billing engine
company backend = adapter/orchestrator
Smart contract company = autorización y ejecución de pago USDC
Chargebee record_payment = reconciliación del pago externoChargebee no procesa el dinero. company ejecuta el pago por fuera y Chargebee solo actualiza el estado contable/billing.