From 12b348afe29c3bebb597cbfc028b787484d6c9e5 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 17 Oct 2023 14:11:04 -0300 Subject: [PATCH 1/3] [Components] Mamo Business #8427 Triggers - New Failed Payment (Instant) - New Successful Payment (Instant) Actions: - Create Payment Link --- .../create-payment-link.mjs | 245 ++++++++++++++++++ components/mamo_business/common/utils.mjs | 6 + .../mamo_business/mamo_business.app.mjs | 46 +++- components/mamo_business/package.json | 7 +- .../mamo_business/sources/common/base.mjs | 56 ++++ .../new-failed-payment/new-failed-payment.mjs | 25 ++ .../sources/new-failed-payment/test-event.mjs | 12 + .../new-successful-payment.mjs | 24 ++ .../new-successful-payment/test-event.mjs | 12 + 9 files changed, 426 insertions(+), 7 deletions(-) create mode 100644 components/mamo_business/actions/create-payment-link/create-payment-link.mjs create mode 100644 components/mamo_business/common/utils.mjs create mode 100644 components/mamo_business/sources/common/base.mjs create mode 100644 components/mamo_business/sources/new-failed-payment/new-failed-payment.mjs create mode 100644 components/mamo_business/sources/new-failed-payment/test-event.mjs create mode 100644 components/mamo_business/sources/new-successful-payment/new-successful-payment.mjs create mode 100644 components/mamo_business/sources/new-successful-payment/test-event.mjs diff --git a/components/mamo_business/actions/create-payment-link/create-payment-link.mjs b/components/mamo_business/actions/create-payment-link/create-payment-link.mjs new file mode 100644 index 0000000000000..8065a1755b55a --- /dev/null +++ b/components/mamo_business/actions/create-payment-link/create-payment-link.mjs @@ -0,0 +1,245 @@ +import { parseObject } from "../../common/utils.mjs"; +import app from "../../mamo_business.app.mjs"; + +export default { + key: "mamo_business-create-payment-link", + name: "Create Payment Link", + version: "0.0.1", + description: "Generate a vanilla or subscription payment link. [See the documentation](https://mamopay.readme.io/reference/post_links)", + type: "action", + props: { + app, + title: { + type: "string", + label: "Title", + description: "The title of the payment link.", + }, + description: { + type: "string", + label: "Description", + description: "Payment description. This will appear on the payment checkout page.", + optional: true, + }, + capacity: { + type: "integer", + label: "Capacity", + description: "The capacity will be ignored when the subscription params exist and value will be null.", + optional: true, + }, + active: { + type: "boolean", + label: "Active", + description: "Whether the payment is active or not.", + optional: true, + }, + returnUrl: { + type: "string", + label: "Return URL", + description: "The URL which the customer will be redirected to after a successful payment.", + optional: true, + }, + failureReturnUrl: { + type: "string", + label: "Failure Return URL", + description: "The URL which the customer will be redirected to after a failure payment.", + optional: true, + }, + processingFeePercentage: { + type: "integer", + label: "Processing Fee Percentage", + description: "The percentage of the transaction that is the fee.", + optional: true, + }, + amount: { + type: "string", + label: "Amount", + description: "The value number of the payment.", + optional: true, + }, + amountCurrency: { + type: "string", + label: "Amount Currency", + description: "The currency that the transaction will be charged.", + default: "AED", + options: [ + "AED", + "USD", + "EUR", + "GBP", + "SAR", + ], + }, + isWidget: { + type: "boolean", + label: "Is Widget", + description: "Generate widget payment link.", + optional: true, + }, + enableTabby: { + type: "boolean", + label: "Enable Tabby", + description: "Enables the ability for customers to buy now and pay later.", + optional: true, + }, + enableMessage: { + type: "boolean", + label: "Enable Message", + description: "Enables the ability for customers to add a message during the checkout process.", + optional: true, + }, + enableTips: { + type: "boolean", + label: "Enable Tips", + description: "Enables the tips option. This will be displayed on the first screen.", + optional: true, + }, + enableCustomerDetails: { + type: "boolean", + label: "Enable Customer Details", + description: "Enables adding customer details such as the name, email, and phone number. This screen will be displayed before the payment details screen.", + optional: true, + }, + enableQuantity: { + type: "boolean", + label: "Enable Quantity", + description: "The number of times a payment link can be used.", + optional: true, + }, + enableQrCode: { + type: "boolean", + label: "Enable QR Code", + description: "Adds the ability to verify a payment through a QR code.", + optional: true, + }, + sendCustomerReceipt: { + type: "boolean", + label: "Send Customer Receipt", + description: "Enables the sending of customer receipts.", + optional: true, + }, + firstName: { + type: "string", + label: "First Name", + description: "The first name of customer which will pre-populate in card info step.", + optional: true, + }, + lastName: { + type: "string", + label: "Last Name", + description: "The last name of customer which will pre-populate in card info step.", + optional: true, + }, + email: { + type: "string", + label: "Email", + description: "The email of customer which will pre-populate in card info step.", + optional: true, + }, + externalId: { + type: "string", + label: "External Id", + description: "The external ID of your choice to associate with payments captured by this payment link.", + optional: true, + }, + isRecurring: { + type: "boolean", + label: "Is Recurring", + description: "Whether this payment link is for a recurring payment.", + reloadProps: true, + }, + }, + async additionalProps() { + const props = {}; + if (this.isRecurring) { + props.frequency = { + type: "string", + label: "Frequency", + description: "Defines the interval that this subscription will be run on.", + options: [ + "weekly", + "monthly", + "annually", + ], + }; + props.frequencyInterval = { + type: "integer", + label: "Frequency Interval", + description: "Defines how often this subscription will run. This will be based on the frequency property defined above.", + }; + props.endDate = { + type: "string", + label: "End Date", + description: "The last date this subscription could run on. Format: YYYY/MM/DD", + }; + props.paymentQuantity = { + type: "string", + label: "Payment Quantity", + description: "Number of times this subscription will occur. If end_date defined, end_date takes precedence.", + }; + } + return props; + }, + async run({ $ }) { + const { + app, + returnUrl, + failureReturnUrl, + processingFeePercentage, + amountCurrency, + isWidget, + enableTabby, + enableMessage, + enableTips, + enableCustomerDetails, + enableQuantity, + enableQrCode, + sendCustomerReceipt, + firstName, + lastName, + externalId, + customData, + isRecurring, + frequency, + frequencyInterval, + endDate, + paymentQuantity, + ...data + } = this; + + const obj = { + return_url: returnUrl, + failure_return_url: failureReturnUrl, + processing_fee_percentage: processingFeePercentage, + amount_currency: amountCurrency, + is_widget: isWidget, + enable_tabby: enableTabby, + enable_message: enableMessage, + enable_tips: enableTips, + enable_customer_details: enableCustomerDetails, + enable_quantity: enableQuantity, + enable_qr_code: enableQrCode, + send_customer_receipt: sendCustomerReceipt, + first_name: firstName, + last_name: lastName, + external_id: externalId, + custom_data: customData && parseObject(customData), + ...data, + }; + if (isRecurring) { + obj.subscription = { + frequency, + frequency_interval: frequencyInterval, + end_ate: endDate, + payment_quantity: paymentQuantity, + }; + } + + const response = await app.createPaymentLink({ + $, + data: obj, + }); + + $.export("$summary", `A new payment link with Id: ${response.id} was successfully created!`); + return response; + }, +}; diff --git a/components/mamo_business/common/utils.mjs b/components/mamo_business/common/utils.mjs new file mode 100644 index 0000000000000..9f49a5c4c8c2f --- /dev/null +++ b/components/mamo_business/common/utils.mjs @@ -0,0 +1,6 @@ +export const parseObject = (obj) => { + if (typeof obj != "object") { + return JSON.parse(obj); + } + return obj; +}; diff --git a/components/mamo_business/mamo_business.app.mjs b/components/mamo_business/mamo_business.app.mjs index fbbfa91e64876..db2295c720698 100644 --- a/components/mamo_business/mamo_business.app.mjs +++ b/components/mamo_business/mamo_business.app.mjs @@ -1,11 +1,47 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "mamo_business", - propDefinitions: {}, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _apiUrl() { + return `https://${this.$auth.environment}.mamopay.com/manage_api/v1`; + }, + _getHeaders() { + return { + "Authorization": `Bearer ${this.$auth.api_key}`, + }; + }, + _makeRequest({ + $ = this, path, ...opts + }) { + const config = { + url: `${this._apiUrl()}/${path}`, + headers: this._getHeaders(), + ...opts, + }; + + return axios($, config); + }, + createHook(args = {}) { + return this._makeRequest({ + method: "POST", + path: "webhooks", + ...args, + }); + }, + createPaymentLink(args = {}) { + return this._makeRequest({ + method: "POST", + path: "links", + ...args, + }); + }, + deleteHook(hookId) { + return this._makeRequest({ + method: "DELETE", + path: `webhooks/${hookId}`, + }); }, }, -}; \ No newline at end of file +}; diff --git a/components/mamo_business/package.json b/components/mamo_business/package.json index 55ce3eeb94015..f3dfd7fd2c978 100644 --- a/components/mamo_business/package.json +++ b/components/mamo_business/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/mamo_business", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Mamo Business Components", "main": "mamo_business.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.5.1" } -} \ No newline at end of file +} diff --git a/components/mamo_business/sources/common/base.mjs b/components/mamo_business/sources/common/base.mjs new file mode 100644 index 0000000000000..678bfa43679f0 --- /dev/null +++ b/components/mamo_business/sources/common/base.mjs @@ -0,0 +1,56 @@ +import app from "../../mamo_business.app.mjs"; + +export default { + dedupe: "unique", + props: { + app, + http: { + type: "$.interface.http", + customResponse: true, + }, + db: "$.service.db", + }, + hooks: { + async activate() { + const data = await this.app.createHook({ + data: { + url: this.http.endpoint, + enabled_events: this.getEvent(), + }, + }); + + this._setHookId(data.id); + }, + async deactivate() { + const id = this._getHookId("hookId"); + await this.app.deleteHook(id); + }, + }, + methods: { + emitEvent(body) { + const meta = this.generateMeta(body); + this.$emit(body, meta); + }, + _getHookId() { + return this.db.get("hookId"); + }, + _setHookId(hookId) { + this.db.set("hookId", hookId); + }, + generateMeta(body) { + return { + id: `${body.id}${body.created_date}`, + summary: this.getSummary(body), + ts: new Date(), + }; + }, + }, + async run({ body }) { + if (body.ping) { + return this.http.respond({ + status: 200, + }); + } + this.emitEvent(body); + }, +}; diff --git a/components/mamo_business/sources/new-failed-payment/new-failed-payment.mjs b/components/mamo_business/sources/new-failed-payment/new-failed-payment.mjs new file mode 100644 index 0000000000000..057811449f02c --- /dev/null +++ b/components/mamo_business/sources/new-failed-payment/new-failed-payment.mjs @@ -0,0 +1,25 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "mamo_business-new-failed-payment", + name: "New Failed Payment (Instant)", + description: "Emit new event when a payment is failed.", + version: "0.0.1", + type: "source", + methods: { + ...common.methods, + getEvent() { + return [ + "charge.failed", + "subscription.failed", + ]; + }, + getSummary(body) { + const { id } = body; + return `A new payment with id ${id} has been failed!`; + }, + }, + sampleEmit, +}; diff --git a/components/mamo_business/sources/new-failed-payment/test-event.mjs b/components/mamo_business/sources/new-failed-payment/test-event.mjs new file mode 100644 index 0000000000000..2590c8406b457 --- /dev/null +++ b/components/mamo_business/sources/new-failed-payment/test-event.mjs @@ -0,0 +1,12 @@ +export default { + "status": "failed", + "id": "MPB-CHRG-E0CE93E071", + "amount": 10, + "refund_amount": 0, + "refund_status": "No refund", + "created_date": "2023-05-31-11-18-57", + "subscription_id": "MPB-SUB-B764EDCBA2", + "settlement_amount": 356.42, + "settlement_currency": "AED", + "settlement_date": "05/06/2023" +}; diff --git a/components/mamo_business/sources/new-successful-payment/new-successful-payment.mjs b/components/mamo_business/sources/new-successful-payment/new-successful-payment.mjs new file mode 100644 index 0000000000000..394fe989df968 --- /dev/null +++ b/components/mamo_business/sources/new-successful-payment/new-successful-payment.mjs @@ -0,0 +1,24 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "mamo_business-new-successful-payment", + name: "New Successful Payment (Instant)", + description: "Emit new event when a payment is charged.", + version: "0.0.1", + type: "source", + methods: { + ...common.methods, + getEvent() { + return [ + "charge.succeeded", + ]; + }, + getSummary(body) { + const { id } = body; + return `A new payment with id ${id} was successfully charged!`; + }, + }, + sampleEmit, +}; diff --git a/components/mamo_business/sources/new-successful-payment/test-event.mjs b/components/mamo_business/sources/new-successful-payment/test-event.mjs new file mode 100644 index 0000000000000..be9d0e596d558 --- /dev/null +++ b/components/mamo_business/sources/new-successful-payment/test-event.mjs @@ -0,0 +1,12 @@ +export default { + "status": "captured", + "id": "MPB-CHRG-E0CE93E071", + "amount": 10, + "refund_amount": 0, + "refund_status": "No refund", + "created_date": "2023-05-31-11-18-57", + "subscription_id": "MPB-SUB-B764EDCBA2", + "settlement_amount": 356.42, + "settlement_currency": "AED", + "settlement_date": "05/06/2023" +}; From e7aafcc63c802fb8dee5632d25d5190ba5c85286 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 17 Oct 2023 14:11:43 -0300 Subject: [PATCH 2/3] pnpm update --- pnpm-lock.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e08023e6929b6..b4edb075d92f6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3193,7 +3193,10 @@ importers: specifiers: {} components/mamo_business: - specifiers: {} + specifiers: + '@pipedream/platform': ^1.5.1 + dependencies: + '@pipedream/platform': 1.5.1 components/manifestly_checklists: specifiers: {} From 75eada8dc95155485c0c988db9f29d7c023f9757 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 17 Oct 2023 17:35:05 -0300 Subject: [PATCH 3/3] fix props --- .../actions/create-payment-link/create-payment-link.mjs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/components/mamo_business/actions/create-payment-link/create-payment-link.mjs b/components/mamo_business/actions/create-payment-link/create-payment-link.mjs index 8065a1755b55a..2a51d40bfe50e 100644 --- a/components/mamo_business/actions/create-payment-link/create-payment-link.mjs +++ b/components/mamo_business/actions/create-payment-link/create-payment-link.mjs @@ -102,7 +102,7 @@ export default { enableQuantity: { type: "boolean", label: "Enable Quantity", - description: "The number of times a payment link can be used.", + description: "Enable the payment link to be used multiple times.", optional: true, }, enableQrCode: { @@ -141,6 +141,12 @@ export default { description: "The external ID of your choice to associate with payments captured by this payment link.", optional: true, }, + customData: { + type: "object", + label: "Custom Data", + description: "An object with custom data of the payment link.", + optional: true, + }, isRecurring: { type: "boolean", label: "Is Recurring",