-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9c89d29
commit 0828c1f
Showing
8 changed files
with
258 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
164 changes: 164 additions & 0 deletions
164
packages/backend/src/apps/paysg/actions/create-payment/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
import type { IJSONArray, IJSONObject, IRawAction } from '@plumber/types' | ||
|
||
import getApiBaseUrl from '../../common/get-api-base-url' | ||
|
||
type CreatePaymentPayload = { | ||
reference_id: string | ||
payer_name: string | ||
payer_address: string | ||
payer_identifier: string | ||
payer_email: string | ||
description: string | ||
amount_in_cents: number | ||
metadata: Record<string, string> | ||
due_date?: string | ||
return_url?: string | ||
} | ||
|
||
function constructPayload(parameters: IJSONObject): CreatePaymentPayload { | ||
const payload: CreatePaymentPayload = { | ||
reference_id: parameters.referenceId as string, | ||
payer_name: parameters.payerName as string, | ||
payer_address: parameters.payerAddress as string, | ||
payer_identifier: parameters.payerIdentifier as string, | ||
payer_email: parameters.payerEmail as string, | ||
description: parameters.description as string, | ||
amount_in_cents: Number(parameters.paymentAmountCents), | ||
metadata: Object.create(null), | ||
} | ||
|
||
if (parameters.dueDate) { | ||
payload['due_date'] = parameters.dueDate as string | ||
} | ||
|
||
if (parameters.returnUrl) { | ||
payload['return_url'] = parameters.returnUrl as string | ||
} | ||
|
||
const metadata = parameters.metadata as IJSONArray | null | ||
if (metadata?.length) { | ||
for (const metadatum of metadata) { | ||
const { key, value } = metadatum as { key: string; value: string } | ||
payload['metadata'][key] = value | ||
} | ||
} | ||
|
||
return payload | ||
} | ||
|
||
const action: IRawAction = { | ||
name: 'Create Payment', | ||
key: 'createPayment', | ||
description: 'Create a new PaySG payment', | ||
arguments: [ | ||
{ | ||
label: 'Reference ID', | ||
key: 'referenceId', | ||
type: 'string' as const, | ||
required: true, | ||
variables: true, | ||
}, | ||
{ | ||
label: 'Payer Name', | ||
key: 'payerName', | ||
type: 'string' as const, | ||
required: true, | ||
variables: true, | ||
}, | ||
{ | ||
label: 'Payer Address', | ||
key: 'payerAddress', | ||
type: 'string' as const, | ||
required: true, | ||
variables: true, | ||
}, | ||
{ | ||
label: 'Payer Identifier', | ||
description: 'e.g. NRIC', | ||
key: 'payerIdentifier', | ||
type: 'string' as const, | ||
required: true, | ||
variables: true, | ||
}, | ||
{ | ||
label: 'Payer Email', | ||
key: 'payerEmail', | ||
type: 'string' as const, | ||
required: true, | ||
variables: true, | ||
}, | ||
{ | ||
label: 'Description', | ||
key: 'description', | ||
type: 'string' as const, | ||
required: true, | ||
variables: true, | ||
}, | ||
{ | ||
label: 'Payment amount (in cents)', | ||
key: 'paymentAmountCents', | ||
type: 'string' as const, | ||
required: true, | ||
variables: true, | ||
}, | ||
{ | ||
label: 'Due Date', | ||
description: 'Must be formatted as DD-MM-YYYY (e.g. 31-DEC-2023)', | ||
key: 'dueDate', | ||
type: 'string' as const, | ||
required: false, | ||
variables: true, | ||
}, | ||
{ | ||
label: 'Return URL', | ||
key: 'returnUrl', | ||
type: 'string' as const, | ||
required: false, | ||
variables: true, | ||
}, | ||
{ | ||
label: 'Metadata', | ||
key: 'metadata', | ||
type: 'multirow' as const, | ||
required: false, | ||
subFields: [ | ||
{ | ||
placeholder: 'Key', | ||
key: 'key', | ||
type: 'string' as const, | ||
required: true, | ||
variables: true, | ||
}, | ||
{ | ||
placeholder: 'Value', | ||
key: 'value', | ||
type: 'string' as const, | ||
required: true, | ||
variables: true, | ||
}, | ||
], | ||
}, | ||
], | ||
|
||
async run($) { | ||
const apiKey = $.auth.data.apiKey as string | ||
const baseUrl = getApiBaseUrl(apiKey) | ||
const paymentServiceId = $.auth.data.paymentServiceId as string | ||
const payload = constructPayload($.step.parameters) | ||
|
||
const response = await $.http.post( | ||
`/v1/payment-services/${paymentServiceId}/payments`, | ||
payload, | ||
{ | ||
baseURL: baseUrl, | ||
headers: { | ||
'x-api-key': apiKey, | ||
}, | ||
}, | ||
) | ||
|
||
$.setActionItem({ raw: { ...response.data } }) | ||
}, | ||
} | ||
|
||
export default action |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import createPayment from './create-payment' | ||
|
||
export default [createPayment] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import isStillVerified from './is-still-verified' | ||
import verifyCredentials from './verify-credentials' | ||
|
||
export default { | ||
fields: [ | ||
{ | ||
key: 'screenName', | ||
label: 'Label', | ||
type: 'string' as const, | ||
required: true, | ||
readOnly: false, | ||
}, | ||
{ | ||
key: 'paymentServiceId', | ||
label: 'Payment Service ID', | ||
type: 'string' as const, | ||
required: true, | ||
readOnly: false, | ||
clickToCopy: false, | ||
}, | ||
{ | ||
key: 'apiKey', | ||
label: 'API key', | ||
type: 'string' as const, | ||
required: true, | ||
readOnly: false, | ||
clickToCopy: false, | ||
autoComplete: 'off' as const, | ||
}, | ||
], | ||
|
||
verifyCredentials, | ||
isStillVerified, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import type { IGlobalVariable } from '@plumber/types' | ||
|
||
import verifyCredentials from './verify-credentials' | ||
|
||
export default async function isStillVerified( | ||
$: IGlobalVariable, | ||
): Promise<boolean> { | ||
await verifyCredentials($) | ||
return true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import type { IGlobalVariable } from '@plumber/types' | ||
|
||
export default async function verifyCredentials( | ||
$: IGlobalVariable, | ||
): Promise<void> { | ||
if (!$.auth.data?.apiKey) { | ||
throw new Error('Invalid PaySG API key') | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
packages/backend/src/apps/paysg/common/get-api-base-url.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
const STAGING_PREFIX = 'paysg_stag_' | ||
const STAGING_BASE_URL = 'https://api-staging.pay.gov.sg' | ||
|
||
const LIVE_PREFIX = 'paysg_live_' | ||
const LIVE_BASE_URL = 'https://api.pay.gov.sg' | ||
|
||
export default function getApiBaseUrl(apiKey: string): string { | ||
if (apiKey.startsWith(LIVE_PREFIX)) { | ||
return LIVE_BASE_URL | ||
} | ||
|
||
if (apiKey.startsWith(STAGING_PREFIX)) { | ||
return STAGING_BASE_URL | ||
} | ||
|
||
throw new Error('API key has unrecognized prefix!') | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import type { IApp } from '@plumber/types' | ||
|
||
import actions from './actions' | ||
import auth from './auth' | ||
|
||
const app: IApp = { | ||
name: 'PaySG', | ||
key: 'paysg', | ||
iconUrl: '{BASE_URL}/apps/paysg/assets/favicon.svg', | ||
authDocUrl: 'https://guide.plumber.gov.sg/user-guides/actions/paysg', | ||
supportsConnections: true, | ||
baseUrl: '', | ||
apiBaseUrl: '', | ||
primaryColor: '000000', | ||
auth, | ||
actions, | ||
} | ||
|
||
export default app |