Skip to content

Commit

Permalink
feat: added toggle for new metadata being sent to webhook (#1200)
Browse files Browse the repository at this point in the history
* add sendAdditionalMetadata

* Moved sendAdditionalMetadata option to webhook schema

* Added metadata handling based on sendAdditionalMetadata property in webhook output

* metadata deletion optionla chaining

* Tidying

* Renamed sendAdditionalMetadata to sendAdditionalPayMetadata

* Updated documentation

---------

Co-authored-by: jen <[email protected]>
  • Loading branch information
ziggy-cyb and jenbutongit authored Feb 20, 2024
1 parent 3c9f2b5 commit d4f2486
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 17 deletions.
30 changes: 23 additions & 7 deletions docs/runner/fee-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,15 +147,31 @@ This will be parsed and sent to GOV.UK Pay as:

When viewing this payment in GOV.UK Pay, you can sort by these columns, and will appear as "metadata" in their interface.

## Other - Reference numbers
## Additional payment metadata

Reference numbers are generated with the alphabet "1234567890ABCDEFGHIJKLMNPQRSTUVWXYZ-_". Note that the letter O is omitted.
With fee options, there is the possibility to send through more payment metadata to your webhook outputs. This metadata will tell your webhook about the status of the payment, the payment id, and the payment reference.

You may configure the length of the reference number by setting the environment variable `PAY_REFERENCE_LENGTH`. The default is 10 characters.
Use [Nano ID Collision Calculator](https://zelark.github.io/nano-id-cc/) to determine the right length for your service.
Since each user will "keep" their own reference number for multiple attempts, calculate the speed at unique users per hour.
If you require this metadata, add the `sendAdditionaPayMetadata` option to your webhook output configuration as below:

e.g. If your service expects 100,000 users per annum, you should expect ~274 users per day, and 11 users per hour.
Using nano-id-cc, and a reference length of 10 characters it will take 102 years, or 9 million IDs generated for a 1% chance of collision.
```json5
{
name: "outputName",
title: "Webhook output",
type: "webhook",
outputConfiguration: {
url: "https://some-url.com",
sendAdditionalPayMetadata: true,
},
}
```

## Other - Reference numbers

Reference numbers are generated with the alphabet "1234567890ABCDEFGHIJKLMNPQRSTUVWXYZ-\_". Note that the letter O is omitted.

You may configure the length of the reference number by setting the environment variable `PAY_REFERENCE_LENGTH`. The default is 10 characters.
Use [Nano ID Collision Calculator](https://zelark.github.io/nano-id-cc/) to determine the right length for your service.
Since each user will "keep" their own reference number for multiple attempts, calculate the speed at unique users per hour.

e.g. If your service expects 100,000 users per annum, you should expect ~274 users per day, and 11 users per hour.
Using nano-id-cc, and a reference length of 10 characters it will take 102 years, or 9 million IDs generated for a 1% chance of collision.
1 change: 1 addition & 0 deletions model/src/data-model/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export type NotifyOutputConfiguration = {

export type WebhookOutputConfiguration = {
url: string;
sendAdditionalPayMetadata?: boolean;
};

export type OutputConfiguration =
Expand Down
1 change: 1 addition & 0 deletions model/src/schema/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ const emailSchema = joi.object().keys({

const webhookSchema = joi.object().keys({
url: joi.string(),
sendAdditionalPayMetadata: joi.boolean().optional().default(false),
allowRetry: joi.boolean().default(true),
});

Expand Down
2 changes: 2 additions & 0 deletions runner/src/server/plugins/engine/models/SummaryViewModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ export class SummaryViewModel {
type: "webhook",
outputData: {
url: output.outputConfiguration.url,
sendAdditionalPayMetadata:
output.outputConfiguration.sendAdditionalPayMetadata,
allowRetry: output.outputConfiguration.allowRetry,
},
};
Expand Down
9 changes: 7 additions & 2 deletions runner/src/server/services/queueStatusService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,13 @@ export class QueueStatusService extends StatusService {

const requests = [
...notify.map((args) => this.notifyService.sendNotification(args)),
...webhook.map(({ url, formData }) =>
this.webhookService.postRequest(url, formData)
...webhook.map(({ url, sendAdditionalPayMetadata, formData }) =>
this.webhookService.postRequest(
url,
formData,
"POST",
sendAdditionalPayMetadata
)
),
];

Expand Down
23 changes: 16 additions & 7 deletions runner/src/server/services/statusService.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HapiRequest, HapiResponseToolkit, HapiServer } from "../types";
import { HapiRequest, HapiServer } from "../types";
import {
CacheService,
NotifyService,
Expand Down Expand Up @@ -151,7 +151,9 @@ export class StatusService {
if (firstWebhook) {
newReference = await this.webhookService.postRequest(
firstWebhook.outputData.url,
formData
{ ...formData },
"POST",
firstWebhook.outputData.sendAdditionalPayMetadata
);
await this.cacheService.mergeState(request, {
reference: newReference,
Expand All @@ -167,8 +169,15 @@ export class StatusService {

const requests = [
...notify.map((args) => this.notifyService.sendNotification(args)),
...webhook.map(({ url, formData }) =>
this.webhookService.postRequest(url, formData)
...webhook.map(({ url, sendAdditionalPayMetadata, formData }) =>
this.webhookService.postRequest(
url,
{
...formData,
},
"POST",
sendAdditionalPayMetadata
)
),
];

Expand Down Expand Up @@ -261,11 +270,11 @@ export class StatusService {
notify.push(args);
}
if (isWebhookModel(currentValue.outputData)) {
const { url } = currentValue.outputData;
webhook.push({ url, formData });
const { url, sendAdditionalPayMetadata } = currentValue.outputData;
webhook.push({ url, sendAdditionalPayMetadata, formData });
this.logger.trace(
["StatusService", "outputArgs", "webhookArgs"],
JSON.stringify({ url, formData })
JSON.stringify({ url, sendAdditionalPayMetadata, formData })
);
}

Expand Down
7 changes: 6 additions & 1 deletion runner/src/server/services/webhookService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,24 @@ export class WebhookService {
* @param url - url of the webhook
* @param data - object to send to the webhook
* @param method - POST or PUT request, defaults to POST
* @param sendAdditionalPayMetadata - whether to include additional metadata in the request
* @returns object with the property `reference` webhook if the response returns with a reference number. If the call fails, the reference will be 'UNKNOWN'.
*/
async postRequest(
url: string,
data: object,
method: "POST" | "PUT" = "POST"
method: "POST" | "PUT" = "POST",
sendAdditionalPayMetadata: boolean = false
) {
this.logger.info(
["WebhookService", "postRequest body"],
JSON.stringify(data)
);
let request = method === "POST" ? post : put;
try {
if (!sendAdditionalPayMetadata) {
delete data?.metadata?.pay;
}
const { payload } = await request(url, {
...DEFAULT_OPTIONS,
payload: JSON.stringify(data),
Expand Down

0 comments on commit d4f2486

Please sign in to comment.