stripe-payment.e2e-spec.ts 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. /* eslint-disable @typescript-eslint/no-non-null-assertion */
  2. import { EntityHydrator, mergeConfig } from '@vendure/core';
  3. import {
  4. CreateProductMutation,
  5. CreateProductMutationVariables,
  6. CreateProductVariantsMutation,
  7. CreateProductVariantsMutationVariables,
  8. TestCreateStockLocationDocument,
  9. } from '@vendure/core/e2e/graphql/generated-e2e-admin-types';
  10. import { CREATE_PRODUCT, CREATE_PRODUCT_VARIANTS } from '@vendure/core/e2e/graphql/shared-definitions';
  11. import { createTestEnvironment, E2E_DEFAULT_CHANNEL_TOKEN } from '@vendure/testing';
  12. import gql from 'graphql-tag';
  13. import nock from 'nock';
  14. import fetch from 'node-fetch';
  15. import path from 'path';
  16. import { Stripe } from 'stripe';
  17. import { afterAll, beforeAll, describe, expect, it } from 'vitest';
  18. import { initialData } from '../../../e2e-common/e2e-initial-data';
  19. import { TEST_SETUP_TIMEOUT_MS, testConfig } from '../../../e2e-common/test-config';
  20. import { StripePlugin } from '../src/stripe';
  21. import { stripePaymentMethodHandler } from '../src/stripe/stripe.handler';
  22. import { CREATE_CHANNEL, CREATE_PAYMENT_METHOD, GET_CUSTOMER_LIST } from './graphql/admin-queries';
  23. import {
  24. CreateChannelMutation,
  25. CreateChannelMutationVariables,
  26. CreatePaymentMethodMutation,
  27. CreatePaymentMethodMutationVariables,
  28. CurrencyCode,
  29. GetCustomerListQuery,
  30. GetCustomerListQueryVariables,
  31. LanguageCode,
  32. } from './graphql/generated-admin-types';
  33. import {
  34. AddItemToOrderMutation,
  35. AddItemToOrderMutationVariables,
  36. GetActiveOrderQuery,
  37. TestOrderFragmentFragment,
  38. } from './graphql/generated-shop-types';
  39. import { ADD_ITEM_TO_ORDER, GET_ACTIVE_ORDER } from './graphql/shop-queries';
  40. import { setShipping } from './payment-helpers';
  41. export const CREATE_STRIPE_PAYMENT_INTENT = gql`
  42. mutation createStripePaymentIntent {
  43. createStripePaymentIntent
  44. }
  45. `;
  46. describe('Stripe payments', () => {
  47. const devConfig = mergeConfig(testConfig(), {
  48. plugins: [
  49. StripePlugin.init({
  50. storeCustomersInStripe: true,
  51. }),
  52. ],
  53. });
  54. const { shopClient, adminClient, server } = createTestEnvironment(devConfig);
  55. let started = false;
  56. let customers: GetCustomerListQuery['customers']['items'];
  57. let order: TestOrderFragmentFragment;
  58. let serverPort: number;
  59. beforeAll(async () => {
  60. serverPort = devConfig.apiOptions.port;
  61. await server.init({
  62. initialData,
  63. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
  64. customerCount: 2,
  65. });
  66. started = true;
  67. await adminClient.asSuperAdmin();
  68. ({
  69. customers: { items: customers },
  70. } = await adminClient.query<GetCustomerListQuery, GetCustomerListQueryVariables>(GET_CUSTOMER_LIST, {
  71. options: {
  72. take: 2,
  73. },
  74. }));
  75. }, TEST_SETUP_TIMEOUT_MS);
  76. afterAll(async () => {
  77. await server.destroy();
  78. });
  79. it('Should start successfully', () => {
  80. expect(started).toEqual(true);
  81. expect(customers).toHaveLength(2);
  82. });
  83. it('Should prepare an order', async () => {
  84. await shopClient.asUserWithCredentials(customers[0].emailAddress, 'test');
  85. const { addItemToOrder } = await shopClient.query<
  86. AddItemToOrderMutation,
  87. AddItemToOrderMutationVariables
  88. >(ADD_ITEM_TO_ORDER, {
  89. productVariantId: 'T_1',
  90. quantity: 2,
  91. });
  92. order = addItemToOrder as TestOrderFragmentFragment;
  93. expect(order.code).toBeDefined();
  94. });
  95. it('Should add a Stripe paymentMethod', async () => {
  96. const { createPaymentMethod } = await adminClient.query<
  97. CreatePaymentMethodMutation,
  98. CreatePaymentMethodMutationVariables
  99. >(CREATE_PAYMENT_METHOD, {
  100. input: {
  101. code: `stripe-payment-${E2E_DEFAULT_CHANNEL_TOKEN}`,
  102. translations: [
  103. {
  104. name: 'Stripe payment test',
  105. description: 'This is a Stripe test payment method',
  106. languageCode: LanguageCode.en,
  107. },
  108. ],
  109. enabled: true,
  110. handler: {
  111. code: stripePaymentMethodHandler.code,
  112. arguments: [
  113. { name: 'apiKey', value: 'test-api-key' },
  114. { name: 'webhookSecret', value: 'test-signing-secret' },
  115. ],
  116. },
  117. },
  118. });
  119. expect(createPaymentMethod.code).toBe(`stripe-payment-${E2E_DEFAULT_CHANNEL_TOKEN}`);
  120. await shopClient.asUserWithCredentials(customers[0].emailAddress, 'test');
  121. await setShipping(shopClient);
  122. });
  123. it('if no customer id exists, makes a call to create', async () => {
  124. let createCustomerPayload: { name: string; email: string } | undefined;
  125. const emptyList = { data: [] };
  126. nock('https://api.stripe.com/')
  127. .get(/\/v1\/customers.*/)
  128. .reply(200, emptyList);
  129. nock('https://api.stripe.com/')
  130. .post('/v1/customers', body => {
  131. createCustomerPayload = body;
  132. return true;
  133. })
  134. .reply(201, {
  135. id: 'new-customer-id',
  136. });
  137. nock('https://api.stripe.com/').post('/v1/payment_intents').reply(200, {
  138. client_secret: 'test-client-secret',
  139. });
  140. const { createStripePaymentIntent } = await shopClient.query(CREATE_STRIPE_PAYMENT_INTENT);
  141. expect(createCustomerPayload).toEqual({
  142. email: 'hayden.zieme12@hotmail.com',
  143. name: 'Hayden Zieme',
  144. });
  145. });
  146. it('should send correct payload to create payment intent', async () => {
  147. let createPaymentIntentPayload: any;
  148. const { activeOrder } = await shopClient.query<GetActiveOrderQuery>(GET_ACTIVE_ORDER);
  149. nock('https://api.stripe.com/')
  150. .post('/v1/payment_intents', body => {
  151. createPaymentIntentPayload = body;
  152. return true;
  153. })
  154. .reply(200, {
  155. client_secret: 'test-client-secret',
  156. });
  157. const { createStripePaymentIntent } = await shopClient.query(CREATE_STRIPE_PAYMENT_INTENT);
  158. expect(createPaymentIntentPayload).toEqual({
  159. amount: activeOrder?.totalWithTax.toString(),
  160. currency: activeOrder?.currencyCode?.toLowerCase(),
  161. customer: 'new-customer-id',
  162. 'automatic_payment_methods[enabled]': 'true',
  163. 'metadata[channelToken]': E2E_DEFAULT_CHANNEL_TOKEN,
  164. 'metadata[orderId]': '1',
  165. 'metadata[orderCode]': activeOrder?.code,
  166. 'metadata[languageCode]': 'en',
  167. });
  168. expect(createStripePaymentIntent).toEqual('test-client-secret');
  169. });
  170. // https://github.com/vendurehq/vendure/issues/1935
  171. it('should attach metadata to stripe payment intent', async () => {
  172. StripePlugin.options.metadata = async (injector, ctx, currentOrder) => {
  173. const hydrator = injector.get(EntityHydrator);
  174. await hydrator.hydrate(ctx, currentOrder, { relations: ['customer'] });
  175. return {
  176. customerEmail: currentOrder.customer?.emailAddress ?? 'demo',
  177. };
  178. };
  179. let createPaymentIntentPayload: any;
  180. const { activeOrder } = await shopClient.query<GetActiveOrderQuery>(GET_ACTIVE_ORDER);
  181. nock('https://api.stripe.com/')
  182. .post('/v1/payment_intents', body => {
  183. createPaymentIntentPayload = body;
  184. return true;
  185. })
  186. .reply(200, {
  187. client_secret: 'test-client-secret',
  188. });
  189. const { createStripePaymentIntent } = await shopClient.query(CREATE_STRIPE_PAYMENT_INTENT);
  190. expect(createPaymentIntentPayload).toEqual({
  191. amount: activeOrder?.totalWithTax.toString(),
  192. currency: activeOrder?.currencyCode?.toLowerCase(),
  193. customer: 'new-customer-id',
  194. 'automatic_payment_methods[enabled]': 'true',
  195. 'metadata[channelToken]': E2E_DEFAULT_CHANNEL_TOKEN,
  196. 'metadata[orderId]': '1',
  197. 'metadata[orderCode]': activeOrder?.code,
  198. 'metadata[languageCode]': 'en',
  199. 'metadata[customerEmail]': customers[0].emailAddress,
  200. });
  201. expect(createStripePaymentIntent).toEqual('test-client-secret');
  202. StripePlugin.options.metadata = undefined;
  203. });
  204. // https://github.com/vendurehq/vendure/issues/2412
  205. it('should attach additional params to payment intent using paymentIntentCreateParams', async () => {
  206. StripePlugin.options.paymentIntentCreateParams = async (injector, ctx, currentOrder) => {
  207. const hydrator = injector.get(EntityHydrator);
  208. await hydrator.hydrate(ctx, currentOrder, { relations: ['customer'] });
  209. return {
  210. description: `Order #${currentOrder.code} for ${currentOrder.customer!.emailAddress}`,
  211. };
  212. };
  213. let createPaymentIntentPayload: any;
  214. const { activeOrder } = await shopClient.query<GetActiveOrderQuery>(GET_ACTIVE_ORDER);
  215. nock('https://api.stripe.com/')
  216. .post('/v1/payment_intents', body => {
  217. createPaymentIntentPayload = body;
  218. return true;
  219. })
  220. .reply(200, {
  221. client_secret: 'test-client-secret',
  222. });
  223. const { createStripePaymentIntent } = await shopClient.query(CREATE_STRIPE_PAYMENT_INTENT);
  224. expect(createPaymentIntentPayload).toEqual({
  225. amount: activeOrder?.totalWithTax.toString(),
  226. currency: activeOrder?.currencyCode?.toLowerCase(),
  227. customer: 'new-customer-id',
  228. description: `Order #${activeOrder!.code} for ${activeOrder!.customer!.emailAddress}`,
  229. 'automatic_payment_methods[enabled]': 'true',
  230. 'metadata[channelToken]': E2E_DEFAULT_CHANNEL_TOKEN,
  231. 'metadata[languageCode]': 'en',
  232. 'metadata[orderId]': '1',
  233. 'metadata[orderCode]': activeOrder?.code,
  234. });
  235. expect(createStripePaymentIntent).toEqual('test-client-secret');
  236. StripePlugin.options.paymentIntentCreateParams = undefined;
  237. });
  238. // https://github.com/vendurehq/vendure/issues/3183
  239. it('should attach additional options to payment intent using requestOptions', async () => {
  240. StripePlugin.options.requestOptions = (injector, ctx, currentOrder) => {
  241. return {
  242. stripeAccount: 'acct_connected',
  243. };
  244. };
  245. let connectedAccountHeader: any;
  246. let createPaymentIntentPayload: any;
  247. const { activeOrder } = await shopClient.query<GetActiveOrderQuery>(GET_ACTIVE_ORDER);
  248. nock('https://api.stripe.com/', {
  249. reqheaders: {
  250. 'Stripe-Account': headerValue => {
  251. connectedAccountHeader = headerValue;
  252. return true;
  253. },
  254. },
  255. })
  256. .post('/v1/payment_intents', body => {
  257. createPaymentIntentPayload = body;
  258. return true;
  259. })
  260. .reply(200, {
  261. client_secret: 'test-client-secret',
  262. });
  263. const { createStripePaymentIntent } = await shopClient.query(CREATE_STRIPE_PAYMENT_INTENT);
  264. expect(createPaymentIntentPayload).toEqual({
  265. amount: activeOrder?.totalWithTax.toString(),
  266. currency: activeOrder?.currencyCode?.toLowerCase(),
  267. customer: 'new-customer-id',
  268. 'automatic_payment_methods[enabled]': 'true',
  269. 'metadata[channelToken]': E2E_DEFAULT_CHANNEL_TOKEN,
  270. 'metadata[orderId]': '1',
  271. 'metadata[languageCode]': 'en',
  272. 'metadata[orderCode]': activeOrder?.code,
  273. });
  274. expect(connectedAccountHeader).toEqual('acct_connected');
  275. expect(createStripePaymentIntent).toEqual('test-client-secret');
  276. StripePlugin.options.paymentIntentCreateParams = undefined;
  277. });
  278. // https://github.com/vendurehq/vendure/issues/2412
  279. it('should attach additional params to customer using customerCreateParams', async () => {
  280. StripePlugin.options.customerCreateParams = async (injector, ctx, currentOrder) => {
  281. const hydrator = injector.get(EntityHydrator);
  282. await hydrator.hydrate(ctx, currentOrder, { relations: ['customer'] });
  283. return {
  284. description: `Description for ${currentOrder.customer!.emailAddress}`,
  285. phone: '12345',
  286. };
  287. };
  288. await shopClient.asUserWithCredentials(customers[1].emailAddress, 'test');
  289. const { addItemToOrder } = await shopClient.query<
  290. AddItemToOrderMutation,
  291. AddItemToOrderMutationVariables
  292. >(ADD_ITEM_TO_ORDER, {
  293. productVariantId: 'T_1',
  294. quantity: 2,
  295. });
  296. order = addItemToOrder as TestOrderFragmentFragment;
  297. let createCustomerPayload: { name: string; email: string } | undefined;
  298. const emptyList = { data: [] };
  299. nock('https://api.stripe.com/')
  300. .get(/\/v1\/customers.*/)
  301. .reply(200, emptyList);
  302. nock('https://api.stripe.com/')
  303. .post('/v1/customers', body => {
  304. createCustomerPayload = body;
  305. return true;
  306. })
  307. .reply(201, {
  308. id: 'new-customer-id',
  309. });
  310. nock('https://api.stripe.com/').post('/v1/payment_intents').reply(200, {
  311. client_secret: 'test-client-secret',
  312. });
  313. const { activeOrder } = await shopClient.query<GetActiveOrderQuery>(GET_ACTIVE_ORDER);
  314. await shopClient.query(CREATE_STRIPE_PAYMENT_INTENT);
  315. expect(createCustomerPayload).toEqual({
  316. email: 'trevor_donnelly96@hotmail.com',
  317. name: 'Trevor Donnelly',
  318. description: `Description for ${activeOrder!.customer!.emailAddress}`,
  319. phone: '12345',
  320. });
  321. });
  322. // https://github.com/vendurehq/vendure/issues/2450
  323. it('Should not crash on signature validation failure', async () => {
  324. const MOCKED_WEBHOOK_PAYLOAD = {
  325. id: 'evt_0',
  326. object: 'event',
  327. api_version: '2022-11-15',
  328. data: {
  329. object: {
  330. id: 'pi_0',
  331. currency: 'usd',
  332. status: 'succeeded',
  333. },
  334. },
  335. livemode: false,
  336. pending_webhooks: 1,
  337. request: {
  338. id: 'req_0',
  339. idempotency_key: '00000000-0000-0000-0000-000000000000',
  340. },
  341. type: 'payment_intent.succeeded',
  342. };
  343. const payloadString = JSON.stringify(MOCKED_WEBHOOK_PAYLOAD, null, 2);
  344. const result = await fetch(`http://localhost:${serverPort}/payments/stripe`, {
  345. method: 'post',
  346. body: payloadString,
  347. headers: { 'Content-Type': 'application/json' },
  348. });
  349. // We didn't provided any signatures, it should result in a 400 - Bad request
  350. expect(result.status).toEqual(400);
  351. });
  352. // TODO: Contribution welcome: test webhook handling and order settlement
  353. // https://github.com/vendurehq/vendure/issues/2450
  354. it("Should validate the webhook's signature properly", async () => {
  355. await shopClient.asUserWithCredentials(customers[0].emailAddress, 'test');
  356. const { activeOrder } = await shopClient.query<GetActiveOrderQuery>(GET_ACTIVE_ORDER);
  357. order = activeOrder!;
  358. const MOCKED_WEBHOOK_PAYLOAD = {
  359. id: 'evt_0',
  360. object: 'event',
  361. api_version: '2022-11-15',
  362. data: {
  363. object: {
  364. id: 'pi_0',
  365. currency: 'usd',
  366. metadata: {
  367. orderCode: order.code,
  368. orderId: parseInt(order.id.replace('T_', ''), 10),
  369. channelToken: E2E_DEFAULT_CHANNEL_TOKEN,
  370. },
  371. amount_received: order.totalWithTax,
  372. status: 'succeeded',
  373. },
  374. },
  375. livemode: false,
  376. pending_webhooks: 1,
  377. request: {
  378. id: 'req_0',
  379. idempotency_key: '00000000-0000-0000-0000-000000000000',
  380. },
  381. type: 'payment_intent.succeeded',
  382. };
  383. const payloadString = JSON.stringify(MOCKED_WEBHOOK_PAYLOAD, null, 2);
  384. const stripeWebhooks = new Stripe('test-api-secret', { apiVersion: '2023-08-16' }).webhooks;
  385. const header = stripeWebhooks.generateTestHeaderString({
  386. payload: payloadString,
  387. secret: 'test-signing-secret',
  388. });
  389. const event = stripeWebhooks.constructEvent(payloadString, header, 'test-signing-secret');
  390. expect(event.id).to.equal(MOCKED_WEBHOOK_PAYLOAD.id);
  391. await setShipping(shopClient);
  392. // Due to the `this.orderService.transitionToState(...)` fails with the internal lookup by id,
  393. // we need to put the order into `ArrangingPayment` state manually before calling the webhook handler.
  394. // const transitionResult = await adminClient.query(TRANSITION_TO_ARRANGING_PAYMENT, { id: order.id });
  395. // expect(transitionResult.transitionOrderToState.__typename).toBe('Order')
  396. const result = await fetch(`http://localhost:${serverPort}/payments/stripe`, {
  397. method: 'post',
  398. body: payloadString,
  399. headers: { 'Content-Type': 'application/json', 'Stripe-Signature': header },
  400. });
  401. // I would expect to the status to be 200, but at the moment either the
  402. // `orderService.transitionToState()` or the `orderService.addPaymentToOrder()`
  403. // throws an error of 'error.entity-with-id-not-found'
  404. expect(result.status).toEqual(200);
  405. });
  406. // https://github.com/vendurehq/vendure/issues/3249
  407. it('Should skip events without expected metadata, when the plugin option is set', async () => {
  408. StripePlugin.options.skipPaymentIntentsWithoutExpectedMetadata = true;
  409. const MOCKED_WEBHOOK_PAYLOAD = {
  410. id: 'evt_0',
  411. object: 'event',
  412. api_version: '2022-11-15',
  413. data: {
  414. object: {
  415. id: 'pi_0',
  416. currency: 'usd',
  417. metadata: {
  418. dummy: 'not a vendure payload',
  419. },
  420. amount_received: 10000,
  421. status: 'succeeded',
  422. },
  423. },
  424. livemode: false,
  425. pending_webhooks: 1,
  426. request: {
  427. id: 'req_0',
  428. idempotency_key: '00000000-0000-0000-0000-000000000000',
  429. },
  430. type: 'payment_intent.succeeded',
  431. };
  432. const payloadString = JSON.stringify(MOCKED_WEBHOOK_PAYLOAD, null, 2);
  433. const stripeWebhooks = new Stripe('test-api-secret', { apiVersion: '2023-08-16' }).webhooks;
  434. const header = stripeWebhooks.generateTestHeaderString({
  435. payload: payloadString,
  436. secret: 'test-signing-secret',
  437. });
  438. const result = await fetch(`http://localhost:${serverPort}/payments/stripe`, {
  439. method: 'post',
  440. body: payloadString,
  441. headers: { 'Content-Type': 'application/json', 'Stripe-Signature': header },
  442. });
  443. expect(result.status).toEqual(200);
  444. });
  445. // https://github.com/vendurehq/vendure/issues/1630
  446. describe('currencies with no fractional units', () => {
  447. let japanProductId: string;
  448. beforeAll(async () => {
  449. const JAPAN_CHANNEL_TOKEN = 'japan-channel-token';
  450. const { createChannel } = await adminClient.query<
  451. CreateChannelMutation,
  452. CreateChannelMutationVariables
  453. >(CREATE_CHANNEL, {
  454. input: {
  455. code: 'japan-channel',
  456. currencyCode: CurrencyCode.JPY,
  457. token: JAPAN_CHANNEL_TOKEN,
  458. defaultLanguageCode: LanguageCode.en,
  459. defaultShippingZoneId: 'T_1',
  460. defaultTaxZoneId: 'T_1',
  461. pricesIncludeTax: true,
  462. },
  463. });
  464. adminClient.setChannelToken(JAPAN_CHANNEL_TOKEN);
  465. shopClient.setChannelToken(JAPAN_CHANNEL_TOKEN);
  466. const { createStockLocation } = await adminClient.query(TestCreateStockLocationDocument, {
  467. input: {
  468. name: 'Japan warehouse',
  469. },
  470. });
  471. const { createProduct } = await adminClient.query<
  472. CreateProductMutation,
  473. CreateProductMutationVariables
  474. >(CREATE_PRODUCT, {
  475. input: {
  476. translations: [
  477. {
  478. languageCode: LanguageCode.en,
  479. name: 'Channel Product',
  480. slug: 'channel-product',
  481. description: 'Channel product',
  482. },
  483. ],
  484. },
  485. });
  486. const { createProductVariants } = await adminClient.query<
  487. CreateProductVariantsMutation,
  488. CreateProductVariantsMutationVariables
  489. >(CREATE_PRODUCT_VARIANTS, {
  490. input: [
  491. {
  492. productId: createProduct.id,
  493. sku: 'PV1',
  494. optionIds: [],
  495. price: 5000,
  496. stockLevels: [
  497. {
  498. stockLocationId: createStockLocation.id,
  499. stockOnHand: 100,
  500. },
  501. ],
  502. translations: [{ languageCode: LanguageCode.en, name: 'Variant 1' }],
  503. },
  504. ],
  505. });
  506. japanProductId = createProductVariants[0]!.id;
  507. // Create a payment method for the Japan channel
  508. await adminClient.query<CreatePaymentMethodMutation, CreatePaymentMethodMutationVariables>(
  509. CREATE_PAYMENT_METHOD,
  510. {
  511. input: {
  512. code: `stripe-payment-${E2E_DEFAULT_CHANNEL_TOKEN}`,
  513. translations: [
  514. {
  515. name: 'Stripe payment test',
  516. description: 'This is a Stripe test payment method',
  517. languageCode: LanguageCode.en,
  518. },
  519. ],
  520. enabled: true,
  521. handler: {
  522. code: stripePaymentMethodHandler.code,
  523. arguments: [
  524. { name: 'apiKey', value: 'test-api-key' },
  525. { name: 'webhookSecret', value: 'test-signing-secret' },
  526. ],
  527. },
  528. },
  529. },
  530. );
  531. });
  532. it('prepares order', async () => {
  533. await shopClient.asUserWithCredentials(customers[0].emailAddress, 'test');
  534. const { addItemToOrder } = await shopClient.query<
  535. AddItemToOrderMutation,
  536. AddItemToOrderMutationVariables
  537. >(ADD_ITEM_TO_ORDER, {
  538. productVariantId: japanProductId,
  539. quantity: 1,
  540. });
  541. expect((addItemToOrder as any).totalWithTax).toBe(5000);
  542. });
  543. it('sends correct amount when creating payment intent', async () => {
  544. let createPaymentIntentPayload: any;
  545. const { activeOrder } = await shopClient.query<GetActiveOrderQuery>(GET_ACTIVE_ORDER);
  546. nock('https://api.stripe.com/')
  547. .post('/v1/payment_intents', body => {
  548. createPaymentIntentPayload = body;
  549. return true;
  550. })
  551. .reply(200, {
  552. client_secret: 'test-client-secret',
  553. });
  554. const { createStripePaymentIntent } = await shopClient.query(CREATE_STRIPE_PAYMENT_INTENT);
  555. expect(createPaymentIntentPayload.amount).toBe((activeOrder!.totalWithTax / 100).toString());
  556. expect(createPaymentIntentPayload.currency).toBe('jpy');
  557. });
  558. });
  559. });