@fat-zebra/sdk 1.5.10 → 1.5.11-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,7 +8,7 @@ env:
8
8
  PROD_CLOUDFRONT_ID: "E3348QX8Q2J4OV"
9
9
  on:
10
10
  push:
11
- branches: ['main']
11
+ branches: ['main']
12
12
  tags:
13
13
  - 'v*.*.*' # prod tags e.g. v1.5.8
14
14
  - 'v*.*.*-beta.*' # beta tags e.g. v1.5.8-beta.0
@@ -37,15 +37,11 @@ jobs:
37
37
  runs-on: ubuntu-latest
38
38
  env:
39
39
  NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
40
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
41
+ NPM_CLOUDSMITH_REPO_PWD: ${{ secrets.CLOUDSMITH_API_KEY }}
40
42
  steps:
41
43
  - uses: actions/checkout@v4
42
44
  - uses: fatzebra/gh-workflows/build_npm@main
43
- with:
44
- credentials_json: |
45
- {
46
- "GITHUB_TOKEN": "${{ secrets.GITHUB_TOKEN }}",
47
- "NPM_CLOUDSMITH_REPO_PWD": "${{ secrets.CLOUDSMITH_API_KEY }}"
48
- }
49
45
  - name: Build (sandbox)
50
46
  run: npm run build:sandbox
51
47
  - uses: actions/upload-artifact@v4
@@ -62,15 +58,11 @@ jobs:
62
58
  runs-on: ubuntu-latest
63
59
  env:
64
60
  NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
61
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
62
+ NPM_CLOUDSMITH_REPO_PWD: ${{ secrets.CLOUDSMITH_API_KEY }}
65
63
  steps:
66
64
  - uses: actions/checkout@v4
67
65
  - uses: fatzebra/gh-workflows/build_npm@main
68
- with:
69
- credentials_json: |
70
- {
71
- "GITHUB_TOKEN": "${{ secrets.GITHUB_TOKEN }}",
72
- "NPM_CLOUDSMITH_REPO_PWD": "${{ secrets.CLOUDSMITH_API_KEY }}"
73
- }
74
66
  - name: Build (production)
75
67
  run: npm run build:production
76
68
  - uses: actions/upload-artifact@v4
@@ -151,6 +143,15 @@ jobs:
151
143
  --id "${invalidation_id}"
152
144
  echo "Invalidation completed."
153
145
 
146
+ notify-sandbox-dispatch:
147
+ name: Notify (sandbox build)
148
+ needs: deploy-sandbox
149
+ if: github.ref_type == 'tag' && contains(github.ref_name, '-beta.')
150
+ uses: fatzebra/gh-workflows/.github/workflows/send_build_created.yml@main
151
+ with:
152
+ destination_repository: fatzebra/gazelle
153
+ secrets: inherit
154
+
154
155
  # Publish to npm for beta tags and production tags
155
156
  publish-npm-beta:
156
157
  runs-on: ubuntu-latest
@@ -35,15 +35,11 @@ jobs:
35
35
  runs-on: ubuntu-latest
36
36
  env:
37
37
  NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
38
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
39
+ NPM_CLOUDSMITH_REPO_PWD: ${{ secrets.CLOUDSMITH_API_KEY }}
38
40
  steps:
39
41
  - uses: actions/checkout@v4
40
42
  - uses: fatzebra/gh-workflows/build_npm@main
41
- with:
42
- credentials_json: |
43
- {
44
- "GITHUB_TOKEN": "${{ secrets.GITHUB_TOKEN }}",
45
- "NPM_CLOUDSMITH_REPO_PWD": "${{ secrets.CLOUDSMITH_API_KEY }}"
46
- }
47
43
  - name: Build (staging)
48
44
  run: npm run build:staging
49
45
  - uses: actions/upload-artifact@v4
package/CODEOWNERS ADDED
@@ -0,0 +1 @@
1
+ * @fatzebra/cx
package/README.dev.md CHANGED
@@ -122,7 +122,7 @@ There are two npm build scripts in `package.json` for building javascript files.
122
122
 
123
123
  ## Deployment Pipeline
124
124
 
125
- The JS SDK gets built and delpoyed via buildkite https://buildkite.com/fat-zebra/fatzebra-js-sdk. The pipeline is defined in `.buildkite/pipeline.yml`
125
+ The JS SDK gets built and delpoyed via github actions https://github.com/fatzebra/fatzebra-js/actions. The pipeline is defined in `.github/workflows/ci.yml`
126
126
 
127
127
  The deployed SDKs are available from the following URLs.
128
128
 
@@ -195,13 +195,16 @@ Staging is released automatically for each branch (on push)
195
195
 
196
196
  Sandbox deployment will trigger on the main branch and when pushing through a tag with beta in it.
197
197
 
198
- Running this on main will trigger sandbox deployment (as well as npm beta release):
198
+ Running this on main will trigger sandbox deployment (as well as npm beta release):
199
199
 
200
200
  ```shell
201
201
  git tag -a v1.5.10-beta.0 -m "Beta: 1.5.10-beta.0"
202
202
  git push origin v1.5.10-beta.0
203
203
  ```
204
204
 
205
+ Additionally, the pipeline will trigger a cypress integration spec on sandbox which will run here: https://github.com/fatzebra/gazelle/actions
206
+ Ensure this pipeline is green before deploying to production and then post a link of the outcome
207
+
205
208
  ## Production
206
209
 
207
210
  Sandbox deployment will trigger on the main branch and when pushing through a tag WITHOUT beta in it.
@@ -0,0 +1 @@
1
+ export declare function logMethod(target: any, propertyKey: string, descriptor: PropertyDescriptor): PropertyDescriptor;
@@ -0,0 +1,35 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import axios from 'axios';
11
+ import { getLoggerUsername, getTransactionReference } from "./logger-context";
12
+ export function logMethod(target, propertyKey, descriptor) {
13
+ const originalMethod = descriptor.value;
14
+ const LOGGING_ENDPOINT = `${process.env.PAYNOW_BASE_URL}/log_sdk`;
15
+ descriptor.value = function (...args) {
16
+ return __awaiter(this, void 0, void 0, function* () {
17
+ try {
18
+ const username = getLoggerUsername();
19
+ const reference = getTransactionReference();
20
+ axios.post(LOGGING_ENDPOINT, {
21
+ username,
22
+ reference,
23
+ method: propertyKey,
24
+ arguments: args,
25
+ timestamp: new Date().toISOString(),
26
+ });
27
+ }
28
+ catch (error) {
29
+ console.warn('Logging failed for sdk:', error);
30
+ }
31
+ return originalMethod.apply(this, args);
32
+ });
33
+ };
34
+ return descriptor;
35
+ }
package/dist/hpp/hpp.d.ts CHANGED
@@ -42,6 +42,7 @@ declare class Hpp {
42
42
  private customer;
43
43
  private username;
44
44
  private sca;
45
+ private newSca;
45
46
  private cardToken;
46
47
  private postMessageClient;
47
48
  private test;
@@ -49,6 +50,7 @@ declare class Hpp {
49
50
  private headlessLoaded;
50
51
  private headlessPreviouslyLoaded;
51
52
  private iframeLoaded;
53
+ private newThreedsFlow;
52
54
  constructor(config: HppModuleConfig);
53
55
  setListenersAndEmitReady(): void;
54
56
  load(config: HppLoadParams): void;
package/dist/hpp/hpp.js CHANGED
@@ -1,9 +1,18 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
1
7
  import * as bridge from '../shared/bridge-client';
2
8
  import { LocalStorageAccessTokenKey } from '../shared/constants';
3
9
  import { emit, on } from '../shared/event-manager';
4
10
  import { PostMessageClient } from '../shared/post-message-client';
5
11
  import { BridgeEvent, PublicEvent, } from '../shared/types';
6
12
  import * as util from '../shared/util';
13
+ import { logMethod } from "../decorators";
14
+ import { setTransactionReference } from "../logger-context";
15
+ import newSca from "../new_sca";
7
16
  const HPP_DEFAULT_OPTIONS = {
8
17
  enableSca: false,
9
18
  hideButton: false,
@@ -11,6 +20,7 @@ const HPP_DEFAULT_OPTIONS = {
11
20
  };
12
21
  class Hpp {
13
22
  constructor(config) {
23
+ this.newThreedsFlow = false;
14
24
  this.paymentIntent = config.paymentIntent;
15
25
  this.customer = config.customer;
16
26
  this.username = config.username;
@@ -20,25 +30,34 @@ class Hpp {
20
30
  this.headlessLoaded = false;
21
31
  this.headlessPreviouslyLoaded = false;
22
32
  this.iframeLoaded = false;
33
+ this.newSca = new newSca({ username: config.username, environment: process.env.API_ENV });
23
34
  this.postMessageClient = new PostMessageClient({
24
35
  channel: 'sca',
25
36
  target: this.iframe
26
37
  });
27
38
  }
28
39
  setListenersAndEmitReady() {
40
+ const message = {
41
+ channel: 'sca',
42
+ subject: BridgeEvent.READY,
43
+ data: {}
44
+ };
29
45
  if (this.headlessLoaded && this.iframeLoaded) {
30
46
  // initial headless load
31
47
  this.setCrossFramesEventListeners();
32
48
  this.setPublicEventListeners();
33
49
  emit(PublicEvent.HPP_READY);
50
+ this.iframe.contentWindow.postMessage(message, '*');
34
51
  }
35
52
  else if (this.headlessPreviouslyLoaded && this.iframeLoaded) {
53
+ this.iframe.contentWindow.postMessage(message, '*');
36
54
  // subsequent iframe loads after headless has been previously loaded
37
55
  // this caters for the SPA scenario
38
56
  emit(PublicEvent.HPP_READY);
39
57
  }
40
58
  }
41
59
  load(config) {
60
+ setTransactionReference(config.paymentIntent.payment.reference);
42
61
  this.hppOptions = config.options;
43
62
  const { el, isExisting } = bridge.load2(process.env.PAYNOW_BRIDGE_URL);
44
63
  this.headless = el;
@@ -112,6 +131,13 @@ class Hpp {
112
131
  if (this.hppOptions.tokenizeOnly)
113
132
  return;
114
133
  if (this.hppOptions.enableSca) {
134
+ if (this.newThreedsFlow) {
135
+ this.newSca.setup({ card_token: data.token, paymentIntent: this.paymentIntent }).then((data) => {
136
+ console.log("result:", data);
137
+ // continue with 3DS flow
138
+ });
139
+ return; // do not continue execution to old 3DS
140
+ }
115
141
  this.sca.run({
116
142
  cardToken: data.token,
117
143
  customer: this.customer,
@@ -171,6 +197,9 @@ class Hpp {
171
197
  data: data,
172
198
  });
173
199
  };
200
+ handlers[BridgeEvent.NEW_THREEDS_ENABLED] = (data) => {
201
+ this.newThreedsFlow = typeof data === "boolean" ? data : false;
202
+ };
174
203
  this.postMessageClient.setEventListeners(handlers);
175
204
  }
176
205
  setPublicEventListeners() {
@@ -204,4 +233,7 @@ class Hpp {
204
233
  this.headless.contentWindow.postMessage(message, '*');
205
234
  }
206
235
  }
236
+ __decorate([
237
+ logMethod
238
+ ], Hpp.prototype, "createPurchase", null);
207
239
  export { Hpp, HPP_DEFAULT_OPTIONS, };
@@ -0,0 +1,4 @@
1
+ export declare function setLoggerUsername(username: string): void;
2
+ export declare function setTransactionReference(reference: string): void;
3
+ export declare function getLoggerUsername(): string | undefined;
4
+ export declare function getTransactionReference(): string | undefined;
@@ -0,0 +1,14 @@
1
+ let globalLoggerUsername;
2
+ let globalTransactionReference;
3
+ export function setLoggerUsername(username) {
4
+ globalLoggerUsername = username;
5
+ }
6
+ export function setTransactionReference(reference) {
7
+ globalTransactionReference = reference;
8
+ }
9
+ export function getLoggerUsername() {
10
+ return globalLoggerUsername;
11
+ }
12
+ export function getTransactionReference() {
13
+ return globalTransactionReference;
14
+ }
package/dist/main.js CHANGED
@@ -19,8 +19,10 @@ import { Hpp } from './hpp';
19
19
  import ClickToPay from './click_to_pay';
20
20
  import { validateApplePayLoadParams } from "./validation/validators/apple-pay-load-params-button-validator";
21
21
  import { ApplePay } from "./applepay";
22
+ import { setLoggerUsername } from "./logger-context";
22
23
  export default class FatZebra {
23
24
  constructor(config) {
25
+ setLoggerUsername(config.username);
24
26
  this.fzConfig = config;
25
27
  window.MerchantUsername = config.username;
26
28
  this.gatewayClient = new GatewayClient({
@@ -0,0 +1,11 @@
1
+ import { Environment } from "../shared/env";
2
+ import { setupProps, ThreeDSSetupResponse } from "../shared/new_gateway/types";
3
+ declare class newSca {
4
+ private gatewayClient;
5
+ constructor({ username, environment }: {
6
+ username: string;
7
+ environment: Environment;
8
+ });
9
+ setup({ card_token, paymentIntent }: setupProps): Promise<ThreeDSSetupResponse>;
10
+ }
11
+ export default newSca;
@@ -0,0 +1,27 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import NewApiGatewayClient from "../shared/new_gateway/new-api-gateway-client";
11
+ // The newSca class will handle the sca requests: setup, enrol, validate
12
+ // The challenge flow will be handled in here
13
+ class newSca {
14
+ constructor({ username, environment }) {
15
+ this.gatewayClient = new NewApiGatewayClient({
16
+ username,
17
+ environment
18
+ });
19
+ }
20
+ setup({ card_token, paymentIntent }) {
21
+ return __awaiter(this, void 0, void 0, function* () {
22
+ const result = yield this.gatewayClient.setup({ card_token, paymentIntent });
23
+ return result;
24
+ });
25
+ }
26
+ }
27
+ export default newSca;
@@ -0,0 +1,16 @@
1
+ import { AxiosResponse, AxiosInstance } from "axios";
2
+ import { Environment } from "../env";
3
+ import { setupProps } from "./types";
4
+ export type GatewayClientProps = {
5
+ username: string;
6
+ environment?: Environment;
7
+ };
8
+ declare class NewGatewayClient {
9
+ username: string;
10
+ environment?: Environment;
11
+ client: AxiosInstance;
12
+ constructor({ username, environment }: GatewayClientProps);
13
+ /**************** SCA/3DS2 /****************/
14
+ setup({ card_token, paymentIntent }: setupProps): Promise<AxiosResponse<any, any, {}>>;
15
+ }
16
+ export default NewGatewayClient;
@@ -0,0 +1,42 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { RequestHeaderSdkVersion, RequestHeaderMerchantUsername, RequestTimeout, } from "../constants";
11
+ import axios from "axios";
12
+ import { getSdkVersionNumber } from "../util";
13
+ import env, { Environment } from "../env";
14
+ class NewGatewayClient {
15
+ constructor({ username, environment }) {
16
+ this.username = username;
17
+ this.environment = environment || Environment.sandbox;
18
+ const headers = {};
19
+ headers[RequestHeaderSdkVersion] = getSdkVersionNumber();
20
+ headers[RequestHeaderMerchantUsername] = this.username;
21
+ const client = axios.create({
22
+ baseURL: env[this.environment].apiUrl,
23
+ timeout: RequestTimeout,
24
+ headers,
25
+ responseType: "json",
26
+ transformResponse: [
27
+ function (response) {
28
+ return typeof response === "string" ? JSON.parse(response) : response;
29
+ },
30
+ ]
31
+ });
32
+ this.client = client;
33
+ }
34
+ /**************** SCA/3DS2 /****************/
35
+ //
36
+ setup({ card_token, paymentIntent }) {
37
+ return __awaiter(this, void 0, void 0, function* () {
38
+ return this.client.post("/three_d_secure/setup", Object.assign(Object.assign({ merchant_username: this.username, card_token }, paymentIntent.payment), { verification: paymentIntent.verification }));
39
+ });
40
+ }
41
+ }
42
+ export default NewGatewayClient;
@@ -0,0 +1,27 @@
1
+ import { PaymentIntent } from "../types";
2
+ type ClientReferenceInformation = {
3
+ code: string;
4
+ partner?: {
5
+ developerId: string;
6
+ };
7
+ };
8
+ type ConsumerAuthenticationInformation = {
9
+ accessToken: string;
10
+ deviceDataCollectionUrl: string;
11
+ referenceId: string;
12
+ token: string;
13
+ };
14
+ interface ThreeDSSetupResponse {
15
+ data: {
16
+ clientReferenceInformation: ClientReferenceInformation;
17
+ consumerAuthenticationInformation: ConsumerAuthenticationInformation;
18
+ id: string;
19
+ status: "COMPLETED" | "PENDING" | "FAILED" | string;
20
+ submitTimeUtc: string;
21
+ };
22
+ }
23
+ type setupProps = {
24
+ card_token: string;
25
+ paymentIntent: PaymentIntent;
26
+ };
27
+ export type { ThreeDSSetupResponse, setupProps };
@@ -0,0 +1 @@
1
+ export {};
@@ -32,7 +32,9 @@ declare enum BridgeEvent {
32
32
  FORM_VALIDATION_ERROR = "fzi.form_validation_error",
33
33
  FORM_VALIDATION_SUCCESS = "fzi.form_validation_success",
34
34
  BIN_LOOKUP = "fzi.bin_lookup",
35
- CLICK_TO_PAY_TOKENIZATION = "fzi.c2p.tc_res"
35
+ CLICK_TO_PAY_TOKENIZATION = "fzi.c2p.tc_res",
36
+ NEW_THREEDS_ENABLED = "fzi.new_threeds_enabled",
37
+ READY = "fzi.ready"
36
38
  }
37
39
  declare enum PaymentMethodType {
38
40
  CARD = "card",
@@ -34,6 +34,8 @@ var BridgeEvent;
34
34
  BridgeEvent["FORM_VALIDATION_SUCCESS"] = "fzi.form_validation_success";
35
35
  BridgeEvent["BIN_LOOKUP"] = "fzi.bin_lookup";
36
36
  BridgeEvent["CLICK_TO_PAY_TOKENIZATION"] = "fzi.c2p.tc_res";
37
+ BridgeEvent["NEW_THREEDS_ENABLED"] = "fzi.new_threeds_enabled";
38
+ BridgeEvent["READY"] = "fzi.ready";
37
39
  })(BridgeEvent || (BridgeEvent = {}));
38
40
  var PaymentMethodType;
39
41
  (function (PaymentMethodType) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fat-zebra/sdk",
3
- "version": "1.5.10",
3
+ "version": "1.5.11-beta.1",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -70,7 +70,7 @@
70
70
  "ajv": "^8.17.1",
71
71
  "ajv-formats": "^3.0.1",
72
72
  "ajv-keywords": "^5.1.0",
73
- "axios": "^1.11.0",
73
+ "axios": "^1.12.0",
74
74
  "custom-event-polyfill": "^1.0.7",
75
75
  "ts-polyfill": "^3.8.2",
76
76
  "url-template": "^3.1.0"
package/tsconfig.json CHANGED
@@ -6,6 +6,7 @@
6
6
  "noImplicitAny": true,
7
7
  "module": "commonjs",
8
8
  "esModuleInterop": true,
9
+ "experimentalDecorators": true,
9
10
  "resolveJsonModule": true,
10
11
  "jsx": "react",
11
12
  "target": "es5",
@@ -10,6 +10,7 @@
10
10
  "module": "esnext",
11
11
  "moduleResolution": "node",
12
12
  "resolveJsonModule": true,
13
+ "experimentalDecorators": true,
13
14
  "target": "es6",
14
15
  "lib": [
15
16
  "esnext",