Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Separate stripe step operations from actual calls to stripe to allow …
…stubbing Sinon stubs don't work if the method is called from within the same file where it is defined: sinonjs/sinon#1161 This separates the code that handles one or more StripeTransactionSteps from the code that actually makes calls to Stripe.
- Loading branch information
Tana Jukes
committed
Jul 26, 2018
1 parent
b13f746
commit 6ad1518
Showing
3 changed files
with
114 additions
and
110 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import { | ||
StripeTransactionPlanStep, | ||
TransactionPlan, | ||
TransactionPlanStep | ||
} from "../../lambdas/rest/transactions/TransactionPlan"; | ||
import * as giftbitRoutes from "giftbit-cassava-routes"; | ||
import {createRefund, createStripeCharge} from "./stripeTransactions"; | ||
import {LightrailAndMerchantStripeConfig} from "./StripeConfig"; | ||
import {StripeTransactionParty} from "../../model/TransactionRequest"; | ||
import {StripeRestError} from "./StripeRestError"; | ||
import {TransactionPlanError} from "../../lambdas/rest/transactions/TransactionPlanError"; | ||
import {StripeCreateChargeParams} from "./StripeCreateChargeParams"; | ||
import {PaymentSourceForStripeMetadata, StripeSourceForStripeMetadata} from "./PaymentSourceForStripeMetadata"; | ||
import log = require("loglevel"); | ||
|
||
export async function chargeStripeSteps(auth: giftbitRoutes.jwtauth.AuthorizationBadge, stripeConfig: LightrailAndMerchantStripeConfig, plan: TransactionPlan) { | ||
const stripeSteps = plan.steps.filter(step => step.rail === "stripe") as StripeTransactionPlanStep[]; | ||
|
||
try { | ||
for (let step of stripeSteps) { | ||
const stepForStripe = stripeTransactionPlanStepToStripeRequest(auth, step, plan); | ||
|
||
const charge = await createStripeCharge(stepForStripe, stripeConfig.lightrailStripeConfig.secretKey, stripeConfig.merchantStripeConfig.stripe_user_id, step.idempotentStepId); | ||
|
||
// Update transaction plan with charge details | ||
step.chargeResult = charge; | ||
// trace back to the requested payment source that lists the right 'source' and/or 'customer' param | ||
if (plan.paymentSources) { | ||
let stepSource = plan.paymentSources.find( | ||
source => source.rail === "stripe" && | ||
(step.source ? source.source === step.source : true) && | ||
(step.customer ? source.customer === step.customer : true) | ||
) as StripeTransactionParty; | ||
stepSource.chargeId = charge.id; | ||
} | ||
} | ||
// await doFraudCheck(lightrailStripeConfig, merchantStripeConfig, params, charge, evt, auth); | ||
} catch (err) { | ||
// todo: differentiate between stripe errors / db step errors, and fraud check errors once we do fraud checking: rollback if appropriate & make sure message is clear | ||
if ((err as StripeRestError).additionalParams.stripeError) { | ||
throw err; | ||
} else { | ||
throw new TransactionPlanError(`Transaction execution canceled because there was a problem charging Stripe: ${err}`, { | ||
isReplanable: false | ||
}); | ||
} | ||
} | ||
} | ||
|
||
function stripeTransactionPlanStepToStripeRequest(auth: giftbitRoutes.jwtauth.AuthorizationBadge, step: StripeTransactionPlanStep, plan: TransactionPlan): StripeCreateChargeParams { | ||
let stepForStripe: StripeCreateChargeParams = { | ||
amount: -step.amount /* Lightrail treats debits as negative amounts on Steps but Stripe requires a positive amount when charging a credit card. */, | ||
currency: plan.currency, | ||
metadata: { | ||
...plan.metadata, | ||
lightrailTransactionId: plan.id, | ||
lightrailTransactionSources: JSON.stringify(plan.steps | ||
.filter(src => !isCurrentStripeStep(src, step)) | ||
.map(src => condensePaymentSourceForStripeMetadata(src))), | ||
lightrailUserId: auth.giftbitUserId | ||
} | ||
}; | ||
if (step.source) { | ||
stepForStripe.source = step.source; | ||
} | ||
if (step.customer) { | ||
stepForStripe.customer = step.customer; | ||
} | ||
|
||
log.debug("Created stepForStripe: \n" + JSON.stringify(stepForStripe, null, 4)); | ||
return stepForStripe; | ||
} | ||
|
||
export async function rollbackStripeSteps(lightrailStripeSecretKey: string, merchantStripeAccountId: string, steps: StripeTransactionPlanStep[], reason: string): Promise<void> { | ||
try { | ||
for (const step of steps) { | ||
const refund = await createRefund(step, lightrailStripeSecretKey, merchantStripeAccountId, reason); | ||
log.info(`Refunded Stripe charge ${step.chargeResult.id}. Refund: ${JSON.stringify(refund)}.`); | ||
} | ||
} catch (err) { | ||
giftbitRoutes.sentry.sendErrorNotification(err); | ||
throw err; | ||
} | ||
} | ||
|
||
function condensePaymentSourceForStripeMetadata(step: TransactionPlanStep): PaymentSourceForStripeMetadata { | ||
switch (step.rail) { | ||
case "lightrail": | ||
return { | ||
rail: "lightrail", | ||
valueId: step.value.id | ||
}; | ||
case "internal": | ||
return { | ||
rail: "internal", | ||
internalId: step.internalId | ||
}; | ||
case "stripe": | ||
let stripeStep = {rail: "stripe"}; | ||
if (step.source) { | ||
(stripeStep as any).source = step.source; | ||
} | ||
if (step.customer) { | ||
(stripeStep as any).customer = step.customer; | ||
} | ||
return stripeStep as StripeSourceForStripeMetadata; | ||
} | ||
} | ||
|
||
function isCurrentStripeStep(step: TransactionPlanStep, currentStep: StripeTransactionPlanStep): boolean { | ||
return step.rail === "stripe" && step.idempotentStepId === currentStep.idempotentStepId; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters