|
|
@@ -1,11 +1,25 @@
|
|
|
import { LanguageCode } from '@vendure/common/lib/generated-types';
|
|
|
-import { Injector, Logger, PaymentMethodHandler } from '@vendure/core';
|
|
|
+import {
|
|
|
+ Customer,
|
|
|
+ EntityHydrator,
|
|
|
+ Injector,
|
|
|
+ InternalServerError,
|
|
|
+ Logger,
|
|
|
+ Order,
|
|
|
+ PaymentMethodHandler,
|
|
|
+ RequestContext,
|
|
|
+ TransactionalConnection,
|
|
|
+} from '@vendure/core';
|
|
|
+import { ConfigArgValues } from '@vendure/core/src/common/configurable-operation';
|
|
|
+import { BraintreeGateway } from 'braintree';
|
|
|
|
|
|
import { extractMetadataFromTransaction, getGateway } from './braintree-common';
|
|
|
import { BRAINTREE_PLUGIN_OPTIONS, loggerCtx } from './constants';
|
|
|
import { BraintreePluginOptions } from './types';
|
|
|
|
|
|
let options: BraintreePluginOptions;
|
|
|
+let connection: TransactionalConnection;
|
|
|
+let entityHydrator: EntityHydrator;
|
|
|
/**
|
|
|
* The handler for Braintree payments.
|
|
|
*/
|
|
|
@@ -19,33 +33,19 @@ export const braintreePaymentMethodHandler = new PaymentMethodHandler({
|
|
|
},
|
|
|
init(injector: Injector) {
|
|
|
options = injector.get<BraintreePluginOptions>(BRAINTREE_PLUGIN_OPTIONS);
|
|
|
+ connection = injector.get(TransactionalConnection);
|
|
|
+ entityHydrator = injector.get(EntityHydrator);
|
|
|
},
|
|
|
async createPayment(ctx, order, amount, args, metadata) {
|
|
|
const gateway = getGateway(args, options);
|
|
|
+ let customerId: string | undefined;
|
|
|
try {
|
|
|
- const response = await gateway.transaction.sale({
|
|
|
- amount: (amount / 100).toString(10),
|
|
|
- orderId: order.code,
|
|
|
- paymentMethodNonce: metadata.nonce,
|
|
|
- options: {
|
|
|
- submitForSettlement: true,
|
|
|
- },
|
|
|
- });
|
|
|
- if (!response.success) {
|
|
|
- return {
|
|
|
- amount,
|
|
|
- state: 'Declined' as const,
|
|
|
- transactionId: response.transaction.id,
|
|
|
- errorMessage: response.message,
|
|
|
- metadata: extractMetadataFromTransaction(response.transaction),
|
|
|
- };
|
|
|
+ await entityHydrator.hydrate(ctx, order, { relations: ['customer'] });
|
|
|
+ const customer = order.customer;
|
|
|
+ if (options.storeCustomersInBraintree && ctx.activeUserId && customer) {
|
|
|
+ customerId = await getBraintreeCustomerId(ctx, gateway, customer);
|
|
|
}
|
|
|
- return {
|
|
|
- amount,
|
|
|
- state: 'Settled' as const,
|
|
|
- transactionId: response.transaction.id,
|
|
|
- metadata: extractMetadataFromTransaction(response.transaction),
|
|
|
- };
|
|
|
+ return processPayment(ctx, gateway, order, amount, metadata.nonce, customerId);
|
|
|
} catch (e) {
|
|
|
Logger.error(e, loggerCtx);
|
|
|
return {
|
|
|
@@ -81,3 +81,74 @@ export const braintreePaymentMethodHandler = new PaymentMethodHandler({
|
|
|
};
|
|
|
},
|
|
|
});
|
|
|
+
|
|
|
+async function processPayment(
|
|
|
+ ctx: RequestContext,
|
|
|
+ gateway: BraintreeGateway,
|
|
|
+ order: Order,
|
|
|
+ amount: number,
|
|
|
+ paymentMethodNonce: any,
|
|
|
+ customerId: string | undefined,
|
|
|
+) {
|
|
|
+ const response = await gateway.transaction.sale({
|
|
|
+ customerId,
|
|
|
+ amount: (amount / 100).toString(10),
|
|
|
+ orderId: order.code,
|
|
|
+ paymentMethodNonce,
|
|
|
+ options: {
|
|
|
+ submitForSettlement: true,
|
|
|
+ storeInVaultOnSuccess: !!customerId,
|
|
|
+ },
|
|
|
+ });
|
|
|
+ if (!response.success) {
|
|
|
+ return {
|
|
|
+ amount,
|
|
|
+ state: 'Declined' as const,
|
|
|
+ transactionId: response.transaction.id,
|
|
|
+ errorMessage: response.message,
|
|
|
+ metadata: extractMetadataFromTransaction(response.transaction),
|
|
|
+ };
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ amount,
|
|
|
+ state: 'Settled' as const,
|
|
|
+ transactionId: response.transaction.id,
|
|
|
+ metadata: extractMetadataFromTransaction(response.transaction),
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * If the Customer has no braintreeCustomerId, create one, else return the existing braintreeCustomerId.
|
|
|
+ */
|
|
|
+async function getBraintreeCustomerId(
|
|
|
+ ctx: RequestContext,
|
|
|
+ gateway: BraintreeGateway,
|
|
|
+ customer: Customer,
|
|
|
+): Promise<string | undefined> {
|
|
|
+ if (!customer.customFields.braintreeCustomerId) {
|
|
|
+ try {
|
|
|
+ const result = await gateway.customer.create({
|
|
|
+ firstName: customer.firstName,
|
|
|
+ lastName: customer.lastName,
|
|
|
+ email: customer.emailAddress,
|
|
|
+ });
|
|
|
+ if (result.success) {
|
|
|
+ const customerId = result.customer.id;
|
|
|
+ Logger.verbose(`Created Braintree Customer record for customerId ${customer.id}`, loggerCtx);
|
|
|
+ customer.customFields.braintreeCustomerId = customerId;
|
|
|
+ await connection.getRepository(ctx, Customer).save(customer, { reload: false });
|
|
|
+ return customerId;
|
|
|
+ } else {
|
|
|
+ Logger.error(
|
|
|
+ `Failed to create Braintree Customer record for customerId ${customer.id}. View Debug level logs for details.`,
|
|
|
+ loggerCtx,
|
|
|
+ );
|
|
|
+ Logger.debug(JSON.stringify(result.errors, null, 2), loggerCtx);
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ Logger.error(e.message, loggerCtx, e.stack);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return customer.customFields.braintreeCustomerId;
|
|
|
+ }
|
|
|
+}
|