Skip to content

Commit

Permalink
Merge pull request #297 from customer-dynamics/release/change-request…
Browse files Browse the repository at this point in the history
…-items

release: v2.0.0
  • Loading branch information
hendrickson-tyler authored Aug 18, 2024
2 parents 3b3a1f0 + 7c496f5 commit 234d0a7
Show file tree
Hide file tree
Showing 59 changed files with 5,049 additions and 6,482 deletions.
10 changes: 9 additions & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ updates:
interval: 'daily'

# Lambda functions
- package-ecosystem: 'npm'
directory: 'lib/lambda/c3-utils-layer/lib/nodejs'
schedule:
interval: 'daily'
- package-ecosystem: 'npm'
directory: 'lib/lambda/c3-create-payment-request'
schedule:
Expand All @@ -40,7 +44,7 @@ updates:
schedule:
interval: 'daily'
- package-ecosystem: 'npm'
directory: 'lib/lambda/c3-email-receipt'
directory: 'lib/lambda/c3-send-receipt'
schedule:
interval: 'daily'
- package-ecosystem: 'npm'
Expand All @@ -51,3 +55,7 @@ updates:
directory: 'lib/lambda/c3-send-agent-message'
schedule:
interval: 'daily'
- package-ecosystem: 'npm'
directory: 'lib/lambda/c3-validate-entry'
schedule:
interval: 'daily'
3 changes: 2 additions & 1 deletion .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ jobs:
"instanceArn": "placeholder",
"securityKeyId": "placeholder",
"securityKeyCertificateContent": "-----BEGIN CERTIFICATE-----\\n-----END CERTIFICATE-----\\n",
"workspaceApp": true
"workspaceApp": true,
"receiptQueueArn": "placeholder"
},
"c3": {
"env": "dev",
Expand Down
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
"conventionalcommits",
"Inclusivity",
"IVR",
"luhn",
"Nexio",
"Popout",
"Runtimes",
"Softphone",
"Tokenizes",
"Visualforce",
Expand Down
2 changes: 1 addition & 1 deletion bin/c3-amazon-connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ async function getMostRecentGitTag(): Promise<string> {
return stdout.trim();
} catch (error) {
console.error('Error fetching the most recent git tag:', error);
return 'v1.4.0';
return 'v2.0.0';
}
}

Expand Down
3 changes: 2 additions & 1 deletion cdk.context.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"instanceArn": "",
"securityKeyId": "",
"securityKeyCertificateContent": "",
"workspaceApp": true
"workspaceApp": true,
"receiptQueueArn": ""
},
"c3": {
"env": "prod",
Expand Down
13 changes: 7 additions & 6 deletions docs/GETTING-STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,13 @@ In order to facilitate this process, you will need to provide some values to the

##### Amazon Connect

| Value | Description |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `instanceArn` | The full ARN of your Amazon Connect Instance. You can find this in the AWS console and it should look something like `"arn:aws:connect:us-west-2:815407490078:instance/5c1f1fba-d5f1-4155-9e09-496456e58912"`. |
| `securityKeyId` | The ID of the security key that you configured for your Amazon Connect instance. You can find this in the AWS console. |
| `securityKeyCertificateContent` | The full content of the certificate associated with your Amazon Connect security key. Begins with `-----BEGIN CERTIFICATE-----` and ends with`-----END CERTIFICATE-----`. **Note**: This must be contained within a single string with newlines denoted with `\\n`. |
| `workspaceApp` | Whether to create the C3 Payment Request app for the Amazon Connect agent workspace. Defaults to `true`. You may want to set this to `false` if you plan to use the workspace through another interface, like Salesforce. **Note**: This option does nothing if no agent-assisted features are enabled. |
| Value | Description |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `instanceArn` | The full ARN of your Amazon Connect Instance. You can find this in the AWS console and it should look something like `"arn:aws:connect:us-west-2:815407490078:instance/5c1f1fba-d5f1-4155-9e09-496456e58912"`. |
| `securityKeyId` | The ID of the security key that you configured for your Amazon Connect instance. You can find this in the AWS console. |
| `securityKeyCertificateContent` | The full content of the certificate associated with your Amazon Connect security key. Begins with `-----BEGIN CERTIFICATE-----` and ends with`-----END CERTIFICATE-----`. **Note**: This must be contained within a single string with newlines denoted with `\\n`. |
| `workspaceApp` | Whether to create the C3 Payment Request app for the Amazon Connect agent workspace. Defaults to `true`. You may want to set this to `false` if you plan to use the workspace through another interface, like Salesforce. **Note**: This option does nothing if no agent-assisted features are enabled. |
| `receiptQueueArn` | **Optional**. The full ARN of the Amazon Connect queue to transfer to when there is no email present for the customer and they would like a receipt. This is only valid for self-service payments. If not provided, the customer will not be asked to transfer to an agent and will simply not receive a receipt. |

##### C3

Expand Down
93 changes: 79 additions & 14 deletions lib/c3-amazon-connect-stack.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { join } from 'path';
import { SecretValue, Stack, StackProps } from 'aws-cdk-lib';
import { CfnIntegrationAssociation } from 'aws-cdk-lib/aws-connect';
import { Code, CodeSigningConfig, Function } from 'aws-cdk-lib/aws-lambda';
import {
Code,
CodeSigningConfig,
Function,
LayerVersion,
} from 'aws-cdk-lib/aws-lambda';
import { PolicyStatement } from 'aws-cdk-lib/aws-iam';
import { SigningProfile, Platform } from 'aws-cdk-lib/aws-signer';
import { Secret } from 'aws-cdk-lib/aws-secretsmanager';
Expand All @@ -12,6 +17,7 @@ import { Zift } from './payment-gateways/zift';
import { AgentAssistedPaymentIVR, SelfServicePaymentIVR } from './features';
import {
associateLambdaFunctionsWithConnect,
commonLambdaLayerProps,
commonLambdaProps,
} from './helpers/lambda';
import { SubjectLookup } from './features/subject-lookup';
Expand Down Expand Up @@ -46,10 +52,12 @@ export class C3AmazonConnectStack extends Stack {
private codeSigningConfig: CodeSigningConfig;
private c3ApiKeySecret: Secret;
private privateKeySecret: Secret;
private utilsLayer: LayerVersion;
private createPaymentRequestFunction: Function;
private validateEntryFunction: Function;
private tokenizeTransactionFunction: Function;
private submitPaymentFunction: Function;
private emailReceiptFunction: Function;
private sendReceiptFunction: Function;

private agentAssistedIVRResources: AgentAssistedPaymentIVR;

Expand All @@ -70,15 +78,18 @@ export class C3AmazonConnectStack extends Stack {
this.featuresContext.agentAssistedIVR
) {
this.createPrivateKeySecret();
this.createUtilsLayer();
this.createCreatePaymentRequestFunction();
this.createValidateEntryFunction();
this.createTokenizeTransactionFunction();
this.createSubmitPaymentFunction();
this.createEmailReceiptFunction();
this.createSendReceiptFunction();
associateLambdaFunctionsWithConnect(this, [
this.createPaymentRequestFunction,
this.validateEntryFunction,
this.tokenizeTransactionFunction,
this.submitPaymentFunction,
this.emailReceiptFunction,
this.sendReceiptFunction,
]);
}

Expand All @@ -91,7 +102,7 @@ export class C3AmazonConnectStack extends Stack {
this.createPaymentRequestFunction,
this.tokenizeTransactionFunction,
this.submitPaymentFunction,
this.emailReceiptFunction,
this.sendReceiptFunction,
);
}
if (this.featuresContext.agentAssistedIVR) {
Expand All @@ -102,10 +113,11 @@ export class C3AmazonConnectStack extends Stack {
this.codeSigningConfig,
this.c3BaseUrl,
this.c3ApiKeySecret,
this.utilsLayer,
this.createPaymentRequestFunction,
this.tokenizeTransactionFunction,
this.submitPaymentFunction,
this.emailReceiptFunction,
this.sendReceiptFunction,
);
}
if (this.featuresContext.subjectLookup) {
Expand Down Expand Up @@ -247,6 +259,20 @@ export class C3AmazonConnectStack extends Stack {
});
}

/**
* Creates a Lambda layer for utility functions.
*
* This layer is necessary for the Lambda functions to access utility functions that are shared across multiple functions.
*/
private createUtilsLayer(): void {
console.log('Creating layer for utility functions...');
this.utilsLayer = new LayerVersion(this, 'C3UtilsLayer', {
...commonLambdaLayerProps,
description: 'Utility functions for C3 payment processing.',
code: Code.fromAsset(join(__dirname, 'lambda/c3-utils-layer/lib')),
});
}

/**
* Creates a Lambda function for creating a payment request.
*
Expand Down Expand Up @@ -274,6 +300,7 @@ export class C3AmazonConnectStack extends Stack {
codeSigningConfig: this.optionsContext.codeSigning
? this.codeSigningConfig
: undefined,
layers: [this.utilsLayer],
},
);

Expand All @@ -285,6 +312,41 @@ export class C3AmazonConnectStack extends Stack {
this.createPaymentRequestFunction.addToRolePolicy(getSecretValuePolicy);
}

/**
* Creates a Lambda function for validating the customer's entry in the IVR.
*
* This function is necessary to validate credit card and bank account information entered by the customer.
*/
private createValidateEntryFunction(): void {
console.log('Creating function C3ValidateEntry...');
this.validateEntryFunction = new Function(this, 'C3ValidateEntry', {
...commonLambdaProps,
description: "Validates a customer's entry in the C3 payment IVR(s).",
code: Code.fromAsset(join(__dirname, 'lambda/c3-validate-entry')),
environment: {
C3_PRIVATE_KEY_SECRET_ID: this.privateKeySecret.secretName,
CONNECT_SECURITY_KEY_ID: this.amazonConnectContext.securityKeyId,
},
codeSigningConfig: this.optionsContext.codeSigning
? this.codeSigningConfig
: undefined,
layers: [this.utilsLayer],
});

// Create policies for decrypting payment information.
const decryptPolicy = new PolicyStatement({
actions: ['kms:Decrypt'],
resources: ['*'],
});
this.validateEntryFunction.addToRolePolicy(decryptPolicy);

const getSecretValuePolicy = new PolicyStatement({
actions: ['secretsmanager:GetSecretValue'],
resources: [this.privateKeySecret.secretArn],
});
this.validateEntryFunction.addToRolePolicy(getSecretValuePolicy);
}

/**
* Creates a Lambda function for tokenizing payment details.
*
Expand All @@ -308,6 +370,7 @@ export class C3AmazonConnectStack extends Stack {
codeSigningConfig: this.optionsContext.codeSigning
? this.codeSigningConfig
: undefined,
layers: [this.utilsLayer],
},
);

Expand Down Expand Up @@ -359,6 +422,7 @@ export class C3AmazonConnectStack extends Stack {
codeSigningConfig: this.optionsContext.codeSigning
? this.codeSigningConfig
: undefined,
layers: [this.utilsLayer],
});

// Create the policy for getting secret values.
Expand All @@ -370,31 +434,32 @@ export class C3AmazonConnectStack extends Stack {
}

/**
* Creates a Lambda function for sending an email receipt.
* Creates a Lambda function for sending a receipt.
*
* This function is necessary for your payment flow to send an email receipt to the customer using C3 after the payment has been processed.
* This function is necessary for your payment flow to send a receipt to the customer using C3 after the payment has been processed.
*/
private createEmailReceiptFunction(): void {
console.log('Creating function C3EmailReceipt...');
this.emailReceiptFunction = new Function(this, 'C3EmailReceipt', {
private createSendReceiptFunction(): void {
console.log('Creating function C3SendReceipt...');
this.sendReceiptFunction = new Function(this, 'C3SendReceipt', {
...commonLambdaProps,
description: 'Creates a payment request through the C3 API.',
code: Code.fromAsset(join(__dirname, 'lambda/c3-email-receipt')),
description: 'Sends a payment receipt using the C3 API.',
code: Code.fromAsset(join(__dirname, 'lambda/c3-send-receipt')),
environment: {
C3_BASE_URL: this.c3BaseUrl,
C3_API_KEY_SECRET_ID: this.c3ApiKeySecret.secretName,
},
codeSigningConfig: this.optionsContext.codeSigning
? this.codeSigningConfig
: undefined,
layers: [this.utilsLayer],
});

// Create the policy for getting secret values.
const getSecretValuePolicy = new PolicyStatement({
actions: ['secretsmanager:GetSecretValue'],
resources: [this.c3ApiKeySecret.secretArn],
});
this.emailReceiptFunction.addToRolePolicy(getSecretValuePolicy);
this.sendReceiptFunction.addToRolePolicy(getSecretValuePolicy);
}

/**
Expand Down
36 changes: 21 additions & 15 deletions lib/connect/content-transformations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,23 @@ import * as subjectLookupFlow from './flows/c3-subject-lookup-flow.json';
* @param createPaymentRequestLambdaArn The Lambda function that creates a payment request.
* @param tokenizeTransactionLambdaArn The Lambda function that tokenizes a transaction.
* @param submitPaymentLambdaArn The Lambda function that submits a payment.
* @param sendReceiptLambdaArn The Lambda function that sends a receipt.
* @param amazonConnectSecurityKeyId The security key ID for Amazon Connect.
* @param amazonConnectSecurityKeyCertificateContent The security key certificate content for Amazon Connect.
* @param amazonConnectReceiptQueueArn The ARN for the Amazon Connect receipt queue.
* @returns A string representing the content for the base IVR payment flow module.
*/
export function getPaymentIVRFlowModuleContent(
createPaymentRequestLambdaFunction: Function,
tokenizeTransactionLambdaFunction: Function,
submitPaymentLambdaFunction: Function,
emailReceiptLambdaFunction: Function,
sendReceiptLambdaFunction: Function,
amazonConnectSecurityKeyId: string,
amazonConnectSecurityKeyCertificateContent: string,
amazonConnectReceiptQueueArn: string,
) {
let transformedContent = JSON.stringify(flowModuleJson);

// Don't escape quotes.
transformedContent = transformedContent.replace(/\\/g, '');

// Replace Lambda placeholders with actual ARNs.
transformedContent = transformedContent.replace(
/<<createPaymentRequestLambdaArn>>/g,
Expand All @@ -40,8 +40,8 @@ export function getPaymentIVRFlowModuleContent(
submitPaymentLambdaFunction.functionArn,
);
transformedContent = transformedContent.replace(
/<<emailReceiptLambdaArn>>/g,
emailReceiptLambdaFunction.functionArn,
/<<sendReceiptLambdaArn>>/g,
sendReceiptLambdaFunction.functionArn,
);

// Replace Amazon Connect security key placeholders with actual values.
Expand All @@ -53,6 +53,17 @@ export function getPaymentIVRFlowModuleContent(
/<<amazonConnectSecurityKeyCertificateContent>>/g,
amazonConnectSecurityKeyCertificateContent,
);

// Replace the receipt queue ID placeholder with the actual value.
const queueId = amazonConnectReceiptQueueArn.split('/queue/').pop();
if (!queueId) {
throw new Error('Invalid ARN for the receipt queue.');
}
transformedContent = transformedContent.replace(
/<<receiptQueueId>>/g,
queueId,
);

return transformedContent;
}

Expand All @@ -63,6 +74,7 @@ export function getPaymentIVRFlowModuleContent(
* @param createPaymentRequestFunction The Lambda function that creates a payment request.
* @param tokenizeTransactionFunction The Lambda function that tokenizes a transaction.
* @param submitPaymentLambdaFunction The Lambda function that submits a payment.
* @param sendReceiptLambdaFunction The Lambda function that sends a receipt.
* @param amazonConnectSecurityKeyId The security key ID for Amazon Connect.
* @param amazonConnectSecurityKeyCertificateContent The security key certificate content for Amazon Connect.
* @returns A string representing the content for the base IVR payment flow.
Expand All @@ -72,15 +84,12 @@ export function getSelfServicePaymentIVRFlowContent(
createPaymentRequestFunction: Function,
tokenizeTransactionFunction: Function,
submitPaymentLambdaFunction: Function,
emailReceiptLambdaFunction: Function,
sendReceiptLambdaFunction: Function,
amazonConnectSecurityKeyId: string,
amazonConnectSecurityKeyCertificateContent: string,
) {
let transformedContent = JSON.stringify(agentAssistedPaymentIVRFlowJson);

// Don't escape quotes.
transformedContent = transformedContent.replace(/\\/g, '');

// Replace Lambda placeholders with actual ARNs.
transformedContent = transformedContent.replace(
/<<sendAgentMessageLambdaArn>>/g,
Expand All @@ -99,8 +108,8 @@ export function getSelfServicePaymentIVRFlowContent(
submitPaymentLambdaFunction.functionArn,
);
transformedContent = transformedContent.replace(
/<<emailReceiptLambdaArn>>/g,
emailReceiptLambdaFunction.functionArn,
/<<sendReceiptLambdaArn>>/g,
sendReceiptLambdaFunction.functionArn,
);

// Replace Amazon Connect security key placeholders with actual values.
Expand Down Expand Up @@ -128,9 +137,6 @@ export function getSubjectLookupFlowContent(
): string {
let transformedContent = JSON.stringify(subjectLookupFlow);

// Don't escape quotes.
transformedContent = transformedContent.replace(/\\/g, '');

// Replace the placeholders with the actual values.
transformedContent = transformedContent.replace(
/<<subjectLookupLambdaArn>>/g,
Expand Down
Loading

0 comments on commit 234d0a7

Please sign in to comment.