payment-method.e2e-spec.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. import {
  2. DefaultLogger,
  3. dummyPaymentHandler,
  4. LanguageCode,
  5. PaymentMethodEligibilityChecker,
  6. } from '@vendure/core';
  7. import {
  8. createErrorResultGuard,
  9. createTestEnvironment,
  10. E2E_DEFAULT_CHANNEL_TOKEN,
  11. ErrorResultGuard,
  12. } from '@vendure/testing';
  13. import gql from 'graphql-tag';
  14. import path from 'path';
  15. import { initialData } from '../../../e2e-common/e2e-initial-data';
  16. import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
  17. import {
  18. CreateChannel,
  19. CreatePaymentMethod,
  20. CurrencyCode,
  21. GetPaymentMethod,
  22. GetPaymentMethodCheckers,
  23. GetPaymentMethodHandlers,
  24. GetPaymentMethodList,
  25. UpdatePaymentMethod,
  26. } from './graphql/generated-e2e-admin-types';
  27. import {
  28. AddItemToOrder,
  29. AddPaymentToOrder,
  30. ErrorCode,
  31. GetEligiblePaymentMethods,
  32. TestOrderWithPaymentsFragment,
  33. } from './graphql/generated-e2e-shop-types';
  34. import { CREATE_CHANNEL } from './graphql/shared-definitions';
  35. import { ADD_ITEM_TO_ORDER, ADD_PAYMENT, GET_ELIGIBLE_PAYMENT_METHODS } from './graphql/shop-definitions';
  36. import { proceedToArrangingPayment } from './utils/test-order-utils';
  37. const checkerSpy = jest.fn();
  38. const minPriceChecker = new PaymentMethodEligibilityChecker({
  39. code: 'min-price-checker',
  40. description: [{ languageCode: LanguageCode.en, value: 'Min price checker' }],
  41. args: {
  42. minPrice: {
  43. type: 'int',
  44. },
  45. },
  46. check(ctx, order, args) {
  47. checkerSpy();
  48. if (order.totalWithTax >= args.minPrice) {
  49. return true;
  50. } else {
  51. return `Order total too low`;
  52. }
  53. },
  54. });
  55. describe('PaymentMethod resolver', () => {
  56. const orderGuard: ErrorResultGuard<TestOrderWithPaymentsFragment> = createErrorResultGuard(
  57. input => !!input.lines,
  58. );
  59. const { server, adminClient, shopClient } = createTestEnvironment({
  60. ...testConfig,
  61. logger: new DefaultLogger(),
  62. paymentOptions: {
  63. paymentMethodEligibilityCheckers: [minPriceChecker],
  64. paymentMethodHandlers: [dummyPaymentHandler],
  65. },
  66. });
  67. beforeAll(async () => {
  68. await server.init({
  69. initialData,
  70. productsCsvPath: path.join(__dirname, 'fixtures/e2e-products-minimal.csv'),
  71. customerCount: 2,
  72. });
  73. await adminClient.asSuperAdmin();
  74. }, TEST_SETUP_TIMEOUT_MS);
  75. afterAll(async () => {
  76. await server.destroy();
  77. });
  78. it('create', async () => {
  79. const { createPaymentMethod } = await adminClient.query<
  80. CreatePaymentMethod.Mutation,
  81. CreatePaymentMethod.Variables
  82. >(CREATE_PAYMENT_METHOD, {
  83. input: {
  84. code: 'no-checks',
  85. name: 'No Checker',
  86. description: 'This is a test payment method',
  87. enabled: true,
  88. handler: {
  89. code: dummyPaymentHandler.code,
  90. arguments: [{ name: 'automaticSettle', value: 'true' }],
  91. },
  92. },
  93. });
  94. expect(createPaymentMethod).toEqual({
  95. id: 'T_1',
  96. name: 'No Checker',
  97. code: 'no-checks',
  98. description: 'This is a test payment method',
  99. enabled: true,
  100. checker: null,
  101. handler: {
  102. args: [
  103. {
  104. name: 'automaticSettle',
  105. value: 'true',
  106. },
  107. ],
  108. code: 'dummy-payment-handler',
  109. },
  110. });
  111. });
  112. it('update', async () => {
  113. const { updatePaymentMethod } = await adminClient.query<
  114. UpdatePaymentMethod.Mutation,
  115. UpdatePaymentMethod.Variables
  116. >(UPDATE_PAYMENT_METHOD, {
  117. input: {
  118. id: 'T_1',
  119. description: 'modified',
  120. checker: {
  121. code: minPriceChecker.code,
  122. arguments: [{ name: 'minPrice', value: '0' }],
  123. },
  124. handler: {
  125. code: dummyPaymentHandler.code,
  126. arguments: [{ name: 'automaticSettle', value: 'false' }],
  127. },
  128. },
  129. });
  130. expect(updatePaymentMethod).toEqual({
  131. id: 'T_1',
  132. name: 'No Checker',
  133. code: 'no-checks',
  134. description: 'modified',
  135. enabled: true,
  136. checker: {
  137. args: [{ name: 'minPrice', value: '0' }],
  138. code: minPriceChecker.code,
  139. },
  140. handler: {
  141. args: [
  142. {
  143. name: 'automaticSettle',
  144. value: 'false',
  145. },
  146. ],
  147. code: dummyPaymentHandler.code,
  148. },
  149. });
  150. });
  151. it('unset checker', async () => {
  152. const { updatePaymentMethod } = await adminClient.query<
  153. UpdatePaymentMethod.Mutation,
  154. UpdatePaymentMethod.Variables
  155. >(UPDATE_PAYMENT_METHOD, {
  156. input: {
  157. id: 'T_1',
  158. checker: null,
  159. },
  160. });
  161. expect(updatePaymentMethod.checker).toEqual(null);
  162. const { paymentMethod } = await adminClient.query<GetPaymentMethod.Query, GetPaymentMethod.Variables>(
  163. GET_PAYMENT_METHOD,
  164. { id: 'T_1' },
  165. );
  166. expect(paymentMethod.checker).toEqual(null);
  167. });
  168. it('paymentMethodEligibilityCheckers', async () => {
  169. const { paymentMethodEligibilityCheckers } = await adminClient.query<GetPaymentMethodCheckers.Query>(
  170. GET_PAYMENT_METHOD_CHECKERS,
  171. );
  172. expect(paymentMethodEligibilityCheckers).toEqual([
  173. {
  174. code: minPriceChecker.code,
  175. args: [{ name: 'minPrice', type: 'int' }],
  176. },
  177. ]);
  178. });
  179. it('paymentMethodHandlers', async () => {
  180. const { paymentMethodHandlers } = await adminClient.query<GetPaymentMethodHandlers.Query>(
  181. GET_PAYMENT_METHOD_HANDLERS,
  182. );
  183. expect(paymentMethodHandlers).toEqual([
  184. {
  185. code: dummyPaymentHandler.code,
  186. args: [{ name: 'automaticSettle', type: 'boolean' }],
  187. },
  188. ]);
  189. });
  190. describe('eligibility checks', () => {
  191. beforeAll(async () => {
  192. await adminClient.query<CreatePaymentMethod.Mutation, CreatePaymentMethod.Variables>(
  193. CREATE_PAYMENT_METHOD,
  194. {
  195. input: {
  196. code: 'price-check',
  197. name: 'With Min Price Checker',
  198. description: 'Order total must be more than 2k',
  199. enabled: true,
  200. checker: {
  201. code: minPriceChecker.code,
  202. arguments: [{ name: 'minPrice', value: '200000' }],
  203. },
  204. handler: {
  205. code: dummyPaymentHandler.code,
  206. arguments: [{ name: 'automaticSettle', value: 'true' }],
  207. },
  208. },
  209. },
  210. );
  211. await adminClient.query<CreatePaymentMethod.Mutation, CreatePaymentMethod.Variables>(
  212. CREATE_PAYMENT_METHOD,
  213. {
  214. input: {
  215. code: 'disabled-method',
  216. name: 'Disabled ones',
  217. description: 'This method is disabled',
  218. enabled: false,
  219. handler: {
  220. code: dummyPaymentHandler.code,
  221. arguments: [{ name: 'automaticSettle', value: 'true' }],
  222. },
  223. },
  224. },
  225. );
  226. await shopClient.asUserWithCredentials('hayden.zieme12@hotmail.com', 'test');
  227. await shopClient.query<AddItemToOrder.Mutation, AddItemToOrder.Variables>(ADD_ITEM_TO_ORDER, {
  228. productVariantId: 'T_1',
  229. quantity: 1,
  230. });
  231. await proceedToArrangingPayment(shopClient);
  232. });
  233. it('eligiblePaymentMethods', async () => {
  234. const { eligiblePaymentMethods } = await shopClient.query<GetEligiblePaymentMethods.Query>(
  235. GET_ELIGIBLE_PAYMENT_METHODS,
  236. );
  237. expect(eligiblePaymentMethods).toEqual([
  238. {
  239. id: 'T_1',
  240. code: 'no-checks',
  241. isEligible: true,
  242. eligibilityMessage: null,
  243. },
  244. {
  245. id: 'T_2',
  246. code: 'price-check',
  247. isEligible: false,
  248. eligibilityMessage: 'Order total too low',
  249. },
  250. ]);
  251. });
  252. it('addPaymentToOrder does not allow ineligible method', async () => {
  253. checkerSpy.mockClear();
  254. const { addPaymentToOrder } = await shopClient.query<
  255. AddPaymentToOrder.Mutation,
  256. AddPaymentToOrder.Variables
  257. >(ADD_PAYMENT, {
  258. input: {
  259. method: 'price-check',
  260. metadata: {},
  261. },
  262. });
  263. orderGuard.assertErrorResult(addPaymentToOrder);
  264. expect(addPaymentToOrder.errorCode).toBe(ErrorCode.INELIGIBLE_PAYMENT_METHOD_ERROR);
  265. expect(addPaymentToOrder.eligibilityCheckerMessage).toBe('Order total too low');
  266. expect(checkerSpy).toHaveBeenCalledTimes(1);
  267. });
  268. });
  269. describe('channels', () => {
  270. const SECOND_CHANNEL_TOKEN = 'SECOND_CHANNEL_TOKEN';
  271. const THIRD_CHANNEL_TOKEN = 'THIRD_CHANNEL_TOKEN';
  272. beforeAll(async () => {
  273. await adminClient.query<CreateChannel.Mutation, CreateChannel.Variables>(CREATE_CHANNEL, {
  274. input: {
  275. code: 'second-channel',
  276. token: SECOND_CHANNEL_TOKEN,
  277. defaultLanguageCode: LanguageCode.en,
  278. currencyCode: CurrencyCode.GBP,
  279. pricesIncludeTax: true,
  280. defaultShippingZoneId: 'T_1',
  281. defaultTaxZoneId: 'T_1',
  282. },
  283. });
  284. await adminClient.query<CreateChannel.Mutation, CreateChannel.Variables>(CREATE_CHANNEL, {
  285. input: {
  286. code: 'third-channel',
  287. token: THIRD_CHANNEL_TOKEN,
  288. defaultLanguageCode: LanguageCode.en,
  289. currencyCode: CurrencyCode.GBP,
  290. pricesIncludeTax: true,
  291. defaultShippingZoneId: 'T_1',
  292. defaultTaxZoneId: 'T_1',
  293. },
  294. });
  295. });
  296. it('creates a PaymentMethod in channel2', async () => {
  297. adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
  298. const { createPaymentMethod } = await adminClient.query<
  299. CreatePaymentMethod.Mutation,
  300. CreatePaymentMethod.Variables
  301. >(CREATE_PAYMENT_METHOD, {
  302. input: {
  303. code: 'channel-2-method',
  304. name: 'Channel 2 method',
  305. description: 'This is a test payment method',
  306. enabled: true,
  307. handler: {
  308. code: dummyPaymentHandler.code,
  309. arguments: [{ name: 'automaticSettle', value: 'true' }],
  310. },
  311. },
  312. });
  313. expect(createPaymentMethod.code).toBe('channel-2-method');
  314. });
  315. it('method is listed in channel2', async () => {
  316. adminClient.setChannelToken(SECOND_CHANNEL_TOKEN);
  317. const { paymentMethods } = await adminClient.query<GetPaymentMethodList.Query>(
  318. GET_PAYMENT_METHOD_LIST,
  319. );
  320. expect(paymentMethods.totalItems).toBe(1);
  321. expect(paymentMethods.items[0].code).toBe('channel-2-method');
  322. });
  323. it('method is not listed in channel3', async () => {
  324. adminClient.setChannelToken(THIRD_CHANNEL_TOKEN);
  325. const { paymentMethods } = await adminClient.query<GetPaymentMethodList.Query>(
  326. GET_PAYMENT_METHOD_LIST,
  327. );
  328. expect(paymentMethods.totalItems).toBe(0);
  329. });
  330. it('method is listed in default channel', async () => {
  331. adminClient.setChannelToken(E2E_DEFAULT_CHANNEL_TOKEN);
  332. const { paymentMethods } = await adminClient.query<GetPaymentMethodList.Query>(
  333. GET_PAYMENT_METHOD_LIST,
  334. );
  335. expect(paymentMethods.totalItems).toBe(4);
  336. expect(paymentMethods.items.map(i => i.code).sort()).toEqual([
  337. 'channel-2-method',
  338. 'disabled-method',
  339. 'no-checks',
  340. 'price-check',
  341. ]);
  342. });
  343. });
  344. });
  345. export const PAYMENT_METHOD_FRAGMENT = gql`
  346. fragment PaymentMethod on PaymentMethod {
  347. id
  348. code
  349. name
  350. description
  351. enabled
  352. checker {
  353. code
  354. args {
  355. name
  356. value
  357. }
  358. }
  359. handler {
  360. code
  361. args {
  362. name
  363. value
  364. }
  365. }
  366. }
  367. `;
  368. export const CREATE_PAYMENT_METHOD = gql`
  369. mutation CreatePaymentMethod($input: CreatePaymentMethodInput!) {
  370. createPaymentMethod(input: $input) {
  371. ...PaymentMethod
  372. }
  373. }
  374. ${PAYMENT_METHOD_FRAGMENT}
  375. `;
  376. export const UPDATE_PAYMENT_METHOD = gql`
  377. mutation UpdatePaymentMethod($input: UpdatePaymentMethodInput!) {
  378. updatePaymentMethod(input: $input) {
  379. ...PaymentMethod
  380. }
  381. }
  382. ${PAYMENT_METHOD_FRAGMENT}
  383. `;
  384. export const GET_PAYMENT_METHOD_HANDLERS = gql`
  385. query GetPaymentMethodHandlers {
  386. paymentMethodHandlers {
  387. code
  388. args {
  389. name
  390. type
  391. }
  392. }
  393. }
  394. `;
  395. export const GET_PAYMENT_METHOD_CHECKERS = gql`
  396. query GetPaymentMethodCheckers {
  397. paymentMethodEligibilityCheckers {
  398. code
  399. args {
  400. name
  401. type
  402. }
  403. }
  404. }
  405. `;
  406. export const GET_PAYMENT_METHOD = gql`
  407. query GetPaymentMethod($id: ID!) {
  408. paymentMethod(id: $id) {
  409. ...PaymentMethod
  410. }
  411. }
  412. ${PAYMENT_METHOD_FRAGMENT}
  413. `;
  414. export const GET_PAYMENT_METHOD_LIST = gql`
  415. query GetPaymentMethodList($options: PaymentMethodListOptions) {
  416. paymentMethods(options: $options) {
  417. items {
  418. ...PaymentMethod
  419. }
  420. totalItems
  421. }
  422. }
  423. ${PAYMENT_METHOD_FRAGMENT}
  424. `;