Skip to content

Commit 0b992cc

Browse files
committed
[Components] fiserv - new components
1 parent 9cbf1c2 commit 0b992cc

File tree

7 files changed

+372
-61
lines changed

7 files changed

+372
-61
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import app from "../../fiserv.app.mjs";
2+
import utils from "../../common/utils.mjs";
3+
4+
export default {
5+
key: "fiserv-create-checkout",
6+
name: "Create Checkout",
7+
description: "Initiate a payment request by passing all the required parameters. It creates a new payment transaction and returns the redirect URL that includes transaction ID. [See the documentation](https://docs.fiserv.dev/public/reference/postcheckouts).",
8+
version: "0.0.1",
9+
type: "action",
10+
props: {
11+
app,
12+
storeId: {
13+
type: "string",
14+
label: "Store ID",
15+
description: "Store id to be used for processing this payment. It also acts as an identifier for your store to load the checkout pages linked to it. If no checkout pages are found, default payment page will be rendered for that transaction.",
16+
},
17+
merchantTransactionId: {
18+
type: "string",
19+
label: "Merchant Transaction ID",
20+
description: "You can use this parameter to tag a unique identifier to this transaction for future reference.",
21+
optional: true,
22+
},
23+
transactionOrigin: {
24+
type: "string",
25+
label: "Transaction Origin",
26+
description: "This parameter is used to flag the transaction source correctly. The possible values are `ECOM` (if the order was recieved from online shop), `MAIL` & `PHONE`.",
27+
options: [
28+
"ECOM",
29+
"MAIL",
30+
"PHONE",
31+
],
32+
},
33+
transactionType: {
34+
type: "string",
35+
label: "Transaction Type",
36+
description: "You can use this parameter to specify the type of transaction you want to perform. Allowed values are: `SALE`, `PRE-AUTH`, `ZERO-AUTH`",
37+
options: [
38+
"SALE",
39+
"PRE-AUTH",
40+
"ZERO-AUTH",
41+
],
42+
},
43+
transactionAmount: {
44+
type: "object",
45+
label: "Transaction Amount",
46+
description: "Object contains `total` transaction amount, `currency`, tax and discount details. Example: `{\"total\":123,\"currency\":\"EUR\",\"components\":{\"subtotal\":115,\"vat\":3,\"shipping\":2.5}}`",
47+
optional: true,
48+
},
49+
order: {
50+
type: "string",
51+
label: "Order",
52+
description: "Object contains order related details. [See the documentation](https://docs.fiserv.dev/public/reference/postcheckouts).",
53+
optional: true,
54+
},
55+
checkoutSettings: {
56+
type: "string",
57+
label: "Checkout Settings",
58+
description: "Object contains checkout related settings. [See the documentation](https://docs.fiserv.dev/public/reference/postcheckouts).",
59+
default: JSON.stringify({
60+
locale: "en_US",
61+
}),
62+
},
63+
paymentMethodDetails: {
64+
type: "string",
65+
label: "Payment Method Details",
66+
description: "Object contains payment method related details. [See the documentation](https://docs.fiserv.dev/public/reference/postcheckouts).",
67+
default: JSON.stringify({
68+
cards: {
69+
authenticationPreferences: {
70+
challengeIndicator: "01",
71+
skipTra: false,
72+
},
73+
createToken: {
74+
declineDuplicateToken: false,
75+
reusable: true,
76+
toBeUsedFor: "UNSCHEDULED",
77+
},
78+
tokenBasedTransaction: {
79+
transactionSequence: "FIRST",
80+
},
81+
},
82+
sepaDirectDebit: {
83+
transactionSequenceType: "SINGLE",
84+
},
85+
}),
86+
},
87+
},
88+
methods: {
89+
createCheckout(args = {}) {
90+
return this.app.post({
91+
path: "/checkouts",
92+
...args,
93+
});
94+
},
95+
},
96+
async run({ $ }) {
97+
const {
98+
createCheckout,
99+
storeId,
100+
merchantTransactionId,
101+
transactionOrigin,
102+
transactionType,
103+
transactionAmount,
104+
order,
105+
checkoutSettings,
106+
paymentMethodDetails,
107+
} = this;
108+
109+
const response = await createCheckout({
110+
$,
111+
data: {
112+
storeId,
113+
merchantTransactionId,
114+
transactionOrigin,
115+
transactionType,
116+
transactionAmount: utils.parseJson(transactionAmount),
117+
order: utils.parseJson(order),
118+
checkoutSettings: utils.parseJson(checkoutSettings),
119+
paymentMethodDetails: utils.parseJson(paymentMethodDetails),
120+
},
121+
});
122+
123+
$.export("$summary", `Successfully created checkout. Redirect URL: ${response.redirectionUrl}`);
124+
return response;
125+
},
126+
};
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import app from "../../fiserv.app.mjs";
2+
3+
export default {
4+
key: "fiserv-retrieve-checkout-details",
5+
name: "Retrieve Checkout Details",
6+
description: "Retrieve details about a specific checkout using the identifier returned when it was created. [See the documentation](https://docs.fiserv.dev/public/reference/get-checkouts-id).",
7+
version: "0.0.1",
8+
type: "action",
9+
props: {
10+
app,
11+
checkoutId: {
12+
type: "string",
13+
label: "Checkout ID",
14+
description: "The unique identifier for the checkout.",
15+
optional: false,
16+
},
17+
},
18+
methods: {
19+
getCheckoutDetails({
20+
checkoutId, ...args
21+
} = {}) {
22+
return this.app._makeRequest({
23+
path: `/checkouts/${checkoutId}`,
24+
...args,
25+
});
26+
},
27+
},
28+
async run({ $ }) {
29+
const {
30+
getCheckoutDetails,
31+
checkoutId,
32+
} = this;
33+
34+
const response = await getCheckoutDetails({
35+
$,
36+
checkoutId,
37+
});
38+
39+
$.export("$summary", `Successfully retrieved details for checkout ID ${this.checkoutId}`);
40+
return response;
41+
},
42+
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const ENVIRONMENT = {
2+
SANDBOX: "sandbox",
3+
PRODUCTION: "production",
4+
};
5+
6+
const SANDBOX_PATH = "/sandbox";
7+
8+
const API_PATH = {
9+
DEFAULT: "/exp/v1",
10+
PAYMENTS: "/ipp/payments-gateway/v2",
11+
FRAUD: "/ipp/fraud/v1",
12+
};
13+
14+
export default {
15+
ENVIRONMENT,
16+
SANDBOX_PATH,
17+
API_PATH,
18+
};

components/fiserv/common/utils.mjs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
const parseJson = (input) => {
2+
const parse = (value) => {
3+
if (typeof(value) === "string") {
4+
try {
5+
return parseJson(JSON.parse(value));
6+
} catch (e) {
7+
return value;
8+
}
9+
} else if (typeof(value) === "object" && value !== null) {
10+
return Object.entries(value)
11+
.reduce((acc, [
12+
key,
13+
val,
14+
]) => Object.assign(acc, {
15+
[key]: parse(val),
16+
}), {});
17+
}
18+
return value;
19+
};
20+
21+
return parse(input);
22+
};
23+
24+
export default {
25+
parseJson,
26+
};

components/fiserv/fiserv.app.mjs

Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,93 @@
1+
import { v4 as uuid } from "uuid";
2+
import crypto from "crypto";
3+
import { axios } from "@pipedream/platform";
4+
import constants from "./common/constants.mjs";
5+
16
export default {
27
type: "app",
38
app: "fiserv",
4-
propDefinitions: {},
59
methods: {
6-
// this.$auth contains connected account data
7-
authKeys() {
8-
console.log(Object.keys(this.$auth));
10+
getAuth() {
11+
const {
12+
url,
13+
api_key: apiKey,
14+
secret_key: secretKey,
15+
environment = constants.ENVIRONMENT.SANDBOX,
16+
} = this.$auth;
17+
return {
18+
url,
19+
apiKey,
20+
secretKey,
21+
environment,
22+
};
23+
},
24+
getUrl(path, apiPath = constants.API_PATH.DEFAULT) {
25+
const {
26+
url,
27+
environment,
28+
} = this.getAuth();
29+
const baseUrl = environment === constants.ENVIRONMENT.SANDBOX
30+
? `${url}${constants.SANDBOX_PATH}${apiPath}`
31+
: `${url}${apiPath}`;
32+
return `${baseUrl}${path}`;
33+
},
34+
/**
35+
* Example at https://docs.fiserv.dev/public/docs/message-signature#example-of-code
36+
*/
37+
getSignatureHeaders(data) {
38+
const {
39+
apiKey,
40+
secretKey,
41+
environment,
42+
} = this.getAuth();
43+
44+
if (environment === constants.ENVIRONMENT.SANDBOX) {
45+
return;
46+
}
47+
48+
const clientRequestId = uuid();
49+
const timestamp = Date.now().toString();
50+
const requestBody = JSON.stringify(data) || "";
51+
const rawSignature = apiKey + clientRequestId + timestamp + requestBody;
52+
53+
const computedHmac =
54+
crypto.createHmac("sha256", secretKey)
55+
.update(rawSignature)
56+
.digest("base64");
57+
58+
return {
59+
"Client-Request-Id": clientRequestId,
60+
"Message-Signature": computedHmac,
61+
"Timestamp": timestamp,
62+
};
63+
},
64+
getHeaders(headers) {
65+
return {
66+
...headers,
67+
"Content-Type": "application/json",
68+
"Accept": "application/json",
69+
"API-Key": this.$auth.api_key,
70+
};
71+
},
72+
_makeRequest({
73+
$ = this, path, headers, data, apiPath, ...args
74+
} = {}) {
75+
return axios($, {
76+
...args,
77+
debug: true,
78+
url: this.getUrl(path, apiPath),
79+
data,
80+
headers: {
81+
...this.getHeaders(headers),
82+
...this.getSignatureHeaders(data),
83+
},
84+
});
85+
},
86+
post(args = {}) {
87+
return this._makeRequest({
88+
method: "POST",
89+
...args,
90+
});
991
},
1092
},
11-
};
93+
};

components/fiserv/package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pipedream/fiserv",
3-
"version": "0.0.1",
3+
"version": "0.1.0",
44
"description": "Pipedream Fiserv Components",
55
"main": "fiserv.app.mjs",
66
"keywords": [
@@ -11,5 +11,10 @@
1111
"author": "Pipedream <[email protected]> (https://pipedream.com/)",
1212
"publishConfig": {
1313
"access": "public"
14+
},
15+
"dependencies": {
16+
"@pipedream/platform": "3.0.3",
17+
"crypto": "^1.0.1",
18+
"uuid": "^11.0.3"
1419
}
15-
}
20+
}

0 commit comments

Comments
 (0)