(params: {
organizationId: string
userId: string
amountToTopUp: number
stripeCustomerId: string
logger: Logger
})
| 448 | } |
| 449 | |
| 450 | async function processOrgAutoTopupPayment(params: { |
| 451 | organizationId: string |
| 452 | userId: string |
| 453 | amountToTopUp: number |
| 454 | stripeCustomerId: string |
| 455 | logger: Logger |
| 456 | }): Promise<void> { |
| 457 | const { organizationId, userId, amountToTopUp, stripeCustomerId, logger } = |
| 458 | params |
| 459 | const logContext = { organizationId, userId, amountToTopUp, stripeCustomerId } |
| 460 | |
| 461 | // Generate a deterministic operation ID based on organizationId and current time to minute precision |
| 462 | const timestamp = generateOperationIdTimestamp(new Date()) |
| 463 | const idempotencyKey = `org-auto-topup-${organizationId}-${timestamp}` |
| 464 | const operationId = idempotencyKey // Use same ID for both Stripe and our DB |
| 465 | |
| 466 | // Organizations use fixed pricing |
| 467 | const amountInCents = amountToTopUp * CREDIT_PRICING.CENTS_PER_CREDIT |
| 468 | |
| 469 | if (amountInCents <= 0) { |
| 470 | throw new AutoTopupPaymentError('Invalid payment amount calculated') |
| 471 | } |
| 472 | |
| 473 | // Get the payment method to use for this organization |
| 474 | const paymentMethodToUse = await getOrganizationPaymentMethod(params) |
| 475 | |
| 476 | const paymentIntent = await stripeServer.paymentIntents.create( |
| 477 | { |
| 478 | amount: amountInCents, |
| 479 | currency: 'usd', |
| 480 | customer: stripeCustomerId, |
| 481 | payment_method: paymentMethodToUse, |
| 482 | off_session: true, |
| 483 | confirm: true, |
| 484 | description: `Organization auto top-up: ${amountToTopUp.toLocaleString()} credits`, |
| 485 | metadata: { |
| 486 | organization_id: organizationId, |
| 487 | credits: amountToTopUp.toString(), |
| 488 | operationId, |
| 489 | type: 'org-auto-topup', |
| 490 | }, |
| 491 | }, |
| 492 | { |
| 493 | idempotencyKey, // Add Stripe idempotency key |
| 494 | }, |
| 495 | ) |
| 496 | |
| 497 | if (paymentIntent.status !== 'succeeded') { |
| 498 | throw new AutoTopupPaymentError('Payment failed or requires action') |
| 499 | } |
| 500 | |
| 501 | await grantOrganizationCredits({ |
| 502 | ...params, |
| 503 | amount: amountToTopUp, |
| 504 | operationId, |
| 505 | description: `Organization auto top-up of ${amountToTopUp.toLocaleString()} credits`, |
| 506 | expiresAt: null, |
| 507 | }) |
no test coverage detected