|
1 | | -import { InternalServerError } from "common/errors/index.js"; |
| 1 | +import { InternalServerError, ValidationError } from "common/errors/index.js"; |
| 2 | +import { capitalizeFirstLetter } from "common/types/roomRequest.js"; |
2 | 3 | import Stripe from "stripe"; |
3 | 4 |
|
4 | 5 | export type StripeLinkCreateParams = { |
@@ -128,3 +129,115 @@ export const deactivateStripeProduct = async ({ |
128 | 129 | active: false, |
129 | 130 | }); |
130 | 131 | }; |
| 132 | + |
| 133 | +export const getStripePaymentIntentData = async ({ |
| 134 | + stripeClient, |
| 135 | + paymentIntentId, |
| 136 | + stripeApiKey, |
| 137 | +}: { |
| 138 | + paymentIntentId: string; |
| 139 | + stripeApiKey: string; |
| 140 | + stripeClient?: Stripe; |
| 141 | +}) => { |
| 142 | + const stripe = stripeClient || new Stripe(stripeApiKey); |
| 143 | + return await stripe.paymentIntents.retrieve(paymentIntentId); |
| 144 | +}; |
| 145 | + |
| 146 | +export const getPaymentMethodForPaymentIntent = async ({ |
| 147 | + paymentIntentId, |
| 148 | + stripeApiKey, |
| 149 | +}: { |
| 150 | + paymentIntentId: string; |
| 151 | + stripeApiKey: string; |
| 152 | +}) => { |
| 153 | + const stripe = new Stripe(stripeApiKey); |
| 154 | + const paymentIntentData = await getStripePaymentIntentData({ |
| 155 | + paymentIntentId, |
| 156 | + stripeApiKey, |
| 157 | + stripeClient: stripe, |
| 158 | + }); |
| 159 | + if (!paymentIntentData) { |
| 160 | + throw new InternalServerError({ |
| 161 | + internalLog: `Could not find payment intent data for payment intent ID "${paymentIntentId}".`, |
| 162 | + }); |
| 163 | + } |
| 164 | + const paymentMethodId = paymentIntentData.payment_method?.toString(); |
| 165 | + if (!paymentMethodId) { |
| 166 | + throw new InternalServerError({ |
| 167 | + internalLog: `Could not find payment method ID for payment intent ID "${paymentIntentId}".`, |
| 168 | + }); |
| 169 | + } |
| 170 | + const paymentMethodData = |
| 171 | + await stripe.paymentMethods.retrieve(paymentMethodId); |
| 172 | + if (!paymentMethodData) { |
| 173 | + throw new InternalServerError({ |
| 174 | + internalLog: `Could not find payment method data for payment intent ID "${paymentIntentId}".`, |
| 175 | + }); |
| 176 | + } |
| 177 | + return paymentMethodData; |
| 178 | +}; |
| 179 | + |
| 180 | +export const supportedStripePaymentMethods = [ |
| 181 | + "us_bank_account", |
| 182 | + "card", |
| 183 | + "card_present", |
| 184 | +] as const; |
| 185 | +export type SupportedStripePaymentMethod = |
| 186 | + (typeof supportedStripePaymentMethods)[number]; |
| 187 | +export const paymentMethodTypeToFriendlyName: Record< |
| 188 | + SupportedStripePaymentMethod, |
| 189 | + string |
| 190 | +> = { |
| 191 | + us_bank_account: "ACH Direct Debit", |
| 192 | + card: "Credit/Debit Card", |
| 193 | + card_present: "Credit/Debit Card (Card Present)", |
| 194 | +}; |
| 195 | + |
| 196 | +export const cardBrandMap: Record<string, string> = { |
| 197 | + amex: "American Express", |
| 198 | + american_express: "American Express", |
| 199 | + cartes_bancaires: "Cartes Bancaires", |
| 200 | + diners: "Diners Club", |
| 201 | + diners_club: "Diners Club", |
| 202 | + discover: "Discover", |
| 203 | + eftpos_au: "EFTPOS Australia", |
| 204 | + eftpos_australia: "EFTPOS Australia", |
| 205 | + interac: "Interac", |
| 206 | + jcb: "JCB", |
| 207 | + link: "Link", |
| 208 | + mastercard: "Mastercard", |
| 209 | + unionpay: "UnionPay", |
| 210 | + visa: "Visa", |
| 211 | + unknown: "Unknown Brand", |
| 212 | + other: "Unknown Brand", |
| 213 | +}; |
| 214 | + |
| 215 | +export const getPaymentMethodDescriptionString = ({ |
| 216 | + paymentMethod, |
| 217 | + paymentMethodType, |
| 218 | +}: { |
| 219 | + paymentMethod: Stripe.PaymentMethod; |
| 220 | + paymentMethodType: SupportedStripePaymentMethod; |
| 221 | +}) => { |
| 222 | + const friendlyName = paymentMethodTypeToFriendlyName[paymentMethodType]; |
| 223 | + switch (paymentMethodType) { |
| 224 | + case "us_bank_account": |
| 225 | + const bankData = paymentMethod[paymentMethodType]; |
| 226 | + if (!bankData) { |
| 227 | + return null; |
| 228 | + } |
| 229 | + return `${friendlyName} (${bankData.bank_name} ${capitalizeFirstLetter(bankData.account_type || "checking")} ${bankData.last4})`; |
| 230 | + case "card": |
| 231 | + const cardData = paymentMethod[paymentMethodType]; |
| 232 | + if (!cardData) { |
| 233 | + return null; |
| 234 | + } |
| 235 | + return `${friendlyName} (${cardBrandMap[cardData.display_brand || "unknown"]} ending in ${cardData.last4})`; |
| 236 | + case "card_present": |
| 237 | + const cardPresentData = paymentMethod[paymentMethodType]; |
| 238 | + if (!cardPresentData) { |
| 239 | + return null; |
| 240 | + } |
| 241 | + return `${friendlyName} (${cardBrandMap[cardPresentData.brand || "unknown"]} ending in ${cardPresentData.last4})`; |
| 242 | + } |
| 243 | +}; |
0 commit comments