Skip to content

Instantly share code, notes, and snippets.

@jeffdeville
Created August 2, 2021 14:25
Show Gist options
  • Save jeffdeville/c9aae0e9f8637cd522f325d01192cfd2 to your computer and use it in GitHub Desktop.
Save jeffdeville/c9aae0e9f8637cd522f325d01192cfd2 to your computer and use it in GitHub Desktop.
async createPayment({
neatAccountId,
paymentToken,
amount,
paymentId,
rbit,
cardHolder,
providerSettings,
}: CreditCardPaymentRequest): Promise<WePayPayment> {
this.validateRbit(rbit)
this.assertValidAccountSettings(providerSettings)
this.assertValuesInCents(amount)
const token = paymentToken
const { address, phone, transactionDetails } = rbit
const credit_card = {
card_holder: {
address: {
country: address.country,
postal_code: address.postal_code,
},
holder_name: cardHolder.holderName,
email: cardHolder.email,
},
} as CreditCardPaymentMethodForPayments
const rbits = [
this.buildAddressRiskBit(address),
this.buildPhoneRiskBit(phone),
this.buildTransactionDetailsBit(transactionDetails),
]
const cardType = await this.getPaymentType({ token, rbits, credit_card })
const feeAmount = this.calculateFee(cardType, amount)
const paymentsRequest: PaymentsRequest = {
account_id: providerSettings?.wepay?.acctId as string,
amount,
currency: this.switchCurrency(address.country),
fee_amount: feeAmount,
reference_id: paymentId,
payment_method: {
token: { id: paymentToken },
credit_card,
},
custom_data: {
neatAccountId,
neatPaymentId: paymentId,
neatInvoiceId: transactionDetails.purchase_order_id,
},
rbits,
} as unknown as PaymentsRequest
try {
const paymentsApi = new PaymentsApi()
const uniqueKey = uuidv4()
const request = async () =>
paymentsApi.createapayment(
this.appId,
this.appToken,
this.apiVersion,
uniqueKey,
paymentsRequest,
)
const result = await this.sendRetryableRequest(request)
return result.data as unknown as WePayPayment
} catch (error) {
if (error.isAxiosError) {
const { error_message, details } = error.response.data
this.mapPaymentError(error_message, details)
}
throw error
}
}
async sendRetryableRequest<TResult>(
request: () => Promise<AxiosResponse<TResult>>,
attempt: number = 1,
): Promise<AxiosResponse<TResult>> {
try {
return await request()
} catch (error) {
if (!this.isRetryable(error) || attempt >= 3) throw error
// return await setTimeout(request, { 1: 5000, 2: 10000 }[attempt] as number)
await sleep({ 1: 5000, 2: 10000 }[attempt] as number)
return await this.sendRetryableRequest(request, attempt + 1)
}
}
isRetryable(error: AxiosError<any>): boolean {
if (typeof error === 'undefined') return false
if (!error?.isAxiosError) return false
if (typeof error.response === 'undefined') return false
const status = error?.response?.status
if (status >= 500 && status < 600) return true // all 500 errors are retryable
if (status == 429) return true // THROTTLE_EXCEEDED.APPLICATION_REQUEST_THROTTLE_EXCEEDED
if (
(error.response?.data?.details || []).find(
(r: any) => r.reason_code === 'INVALID_PARAMS.CONCURRENT_UNIQUE_KEY_REQUEST_IS_PROCESSING',
)
)
return true
return false
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment