├── .gitignore ├── README.md ├── capture.js ├── charge.js ├── package-lock.json ├── package.json ├── refund.js ├── src ├── capture-charge.js ├── create-charge.js ├── create-refund.js ├── payment-processor.js ├── process-response.js ├── pubsub-repository.js ├── tracing-repository.js └── tracing-repository.ts ├── template.yaml └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm_debug.log 3 | .vscode 4 | .DS_Store 5 | .idea 6 | output.yaml 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # API Gateway -> Lambda -> Stripe Charge Payment (Checkout) 3 | 4 | ## Description 5 | 6 | This is a serverless component consisting of an API and Lambda functions that charge / capture / refund a Stripe account based on the API Gateway request data. Supports CORS. Written in Node.js. 7 | 8 | ## Requirements (Setup) 9 | 10 | 1. Create a Stripe account 11 | 2. Get your Stripe API Keys (both public and secret) 12 | 3. Store your Stripe Secret Key into AWS SSM as a SecureString by running the following, but be sure to replace the `lambda-stripe-charge/stripe-secret-key` with your preferred SSM Parameter Path (though it can be just `lambda-stripe-charge/stripe-secret-key`): 13 | 14 | ```ssh 15 | aws ssm put-parameter --name /lambda-stripe-charge/stripe-secret-key --value YOUR_STRIPE_SECURE_KEY --type SecureString --overwrite 16 | ``` 17 | 18 | 4. Want to use Stripe's Checkout ? - [https://stripe.com/docs/checkout](https://stripe.com/docs/checkout) 19 | (most likely others are supported too, but can't guarantee, need to check) 20 | 5. Set your frontend part as specified in the [https://stripe.com/docs/checkout#integration](https://stripe.com/docs/checkout#integration) 21 | 6. Extend your form with hidden input HTML elements for **amount** and **currency**. Those fields you will need to populate with the values chosen by the user. If not familiar with that approach I recommend this StackOverflow post - [https://stackoverflow.com/questions/37798593/stripe-checkout-how-to-pass-a-value-through-to-webhook](https://stackoverflow.com/questions/37798593/stripe-checkout-how-to-pass-a-value-through-to-webhook) 22 | 23 | Will provide a video link on YouTube soon, as I will stream using this AWS App Repo template. 24 | 25 | ### Latest Release - 4.4.1 26 | 27 | Changing to Node.js 10.x Runtime For Lambda 28 | 29 | #### Previous Release 4.4.0 30 | 31 | - Added Active X-Ray Tracing 32 | -------------------------------------------------------------------------------- /capture.js: -------------------------------------------------------------------------------- 1 | const AWS = require('aws-sdk'), 2 | ssm = new AWS.SSM(), 3 | qs = require('querystring'), 4 | processResponse = require('./src/process-response'), 5 | captureCharge = require('./src/capture-charge'), 6 | STRIPE_SECRET_KEY_NAME = `/${process.env.SSM_PARAMETER_PATH}`, 7 | IS_CORS = true, 8 | stripeSecretKeyValue = ssm.getParameter({ Name: STRIPE_SECRET_KEY_NAME, WithDecryption: true }); 9 | 10 | exports.handler = (event) => { 11 | if (event.httpMethod === 'OPTIONS') { 12 | return Promise.resolve(processResponse(IS_CORS)); 13 | } 14 | if (!event.body) { 15 | return Promise.resolve(processResponse(IS_CORS, 'invalid', 400)); 16 | } 17 | 18 | const captureRequest = typeof event.body == 'object' ? event.body : JSON.parse(event.body); 19 | if (!captureRequest.chargeId) { 20 | return Promise.resolve(processResponse(IS_CORS, 'invalid arguments, please provide the chargeId (its ID) as mentioned in the app README', 400)); 21 | } 22 | 23 | return captureCharge(stripeSecretKeyValue.Parameter.Value, captureRequest.chargeId, captureRequest.email) 24 | .then(capturedCharge => processResponse(IS_CORS, { capturedCharge })) 25 | .catch((err) => { 26 | console.log(err); 27 | return processResponse(IS_CORS, { err }, 500); 28 | }); 29 | }; 30 | -------------------------------------------------------------------------------- /charge.js: -------------------------------------------------------------------------------- 1 | const AWS = require('aws-sdk'), 2 | ssm = new AWS.SSM(), 3 | qs = require('querystring'), 4 | processResponse = require('./src/process-response'), 5 | createCharge = require('./src/create-charge'), 6 | STRIPE_SECRET_KEY_NAME = `/${process.env.SSM_PARAMETER_PATH}`, 7 | IS_CORS = true, 8 | stripeSecretKeyValue = ssm.getParameter({ Name: STRIPE_SECRET_KEY_NAME, WithDecryption: true }); 9 | 10 | exports.handler = (event) => { 11 | if (event.httpMethod === 'OPTIONS') { 12 | return Promise.resolve(processResponse(IS_CORS)); 13 | } 14 | if (!event.body) { 15 | return Promise.resolve(processResponse(IS_CORS, 'invalid', 400)); 16 | } 17 | 18 | const chargeRequest = typeof event.body == 'object' ? event.body : JSON.parse(event.body); 19 | if (!chargeRequest.amount || !chargeRequest.currency) { 20 | return Promise.resolve(processResponse(IS_CORS, 'invalid arguments, please provide amount and currency fields as mentioned in the app README', 400)); 21 | } 22 | 23 | return createCharge(stripeSecretKeyValue.Parameter.value, chargeRequest.stripeToken, chargeRequest.email, chargeRequest.amount, chargeRequest.currency, chargeRequest.description) 24 | .then(createdCharge => processResponse(IS_CORS, { createdCharge })) 25 | .catch((err) => { 26 | console.log(err); 27 | return processResponse(IS_CORS, { err }, 500); 28 | }); 29 | }; 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api-lambda-stripe-charge", 3 | "version": "4.4.2", 4 | "description": "", 5 | "main": "charge.js", 6 | "scripts": { 7 | "test": "jest --color", 8 | "prepack": "npm install --production", 9 | "prepackage": "tsc", 10 | "package": "aws cloudformation package --template-file template.yaml --output-template-file output.yaml --s3-bucket app-repo-components", 11 | "deploy": "aws cloudformation deploy --template-file output.yaml --stack-name api-lambda-stripe-charge --capabilities CAPABILITY_IAM --parameter-overrides SSMParameterPath=lambda-stripe-charge/stripe-secret-key EnableInstantCapture=false", 12 | "describe": "aws cloudformation describe-stacks --stack-name api-lambda-stripe-charge --query Stacks[].Outputs[].OutputValue --output text", 13 | "qd": "npm run prepack && npm run package && npm run deploy && npm run describe" 14 | }, 15 | "keywords": [], 16 | "author": "Aleksandar Simovic ", 17 | "license": "MIT", 18 | "jest": { 19 | "roots": [ 20 | "/src/", 21 | "/tests/" 22 | ] 23 | }, 24 | "dependencies": { 25 | "aws-xray-sdk": "^2.3.4", 26 | "stripe": "^7.3.0" 27 | }, 28 | "devDependencies": { 29 | "@types/node": "^8.10.38", 30 | "jest": "^24.8.0", 31 | "typescript": "^3.2.4" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /refund.js: -------------------------------------------------------------------------------- 1 | const AWS = require('aws-sdk'), 2 | ssm = new AWS.SSM(), 3 | processResponse = require('./src/process-response'), 4 | createRefund = require('./src/create-refund'), 5 | STRIPE_SECRET_KEY_NAME = `/${process.env.SSM_PARAMETER_PATH}`, 6 | IS_CORS = true, 7 | stripeSecretKeyValue = ssm.getParameter({ Name: STRIPE_SECRET_KEY_NAME, WithDecryption: true }); 8 | 9 | exports.handler = (event) => { 10 | if (event.httpMethod === 'OPTIONS') { 11 | return Promise.resolve(processResponse(IS_CORS)); 12 | } 13 | if (!event.body) { 14 | return Promise.resolve(processResponse(IS_CORS, 'invalid', 400)); 15 | } 16 | 17 | const refundRequest = typeof event.body == 'object' ? event.body : JSON.parse(event.body); 18 | if (!refundRequest.chargeId) { 19 | return Promise.resolve(processResponse(IS_CORS, 'invalid arguments, please provide the chargeId (its ID) as mentioned in the app README', 400)); 20 | } 21 | 22 | return createRefund(stripeSecretKeyValue.Parameter.Value, refundRequest.chargeId, refundRequest.email) 23 | .then(createdRefund => processResponse(IS_CORS, { createdRefund })) 24 | .catch((err) => { 25 | console.log(err); 26 | return processResponse(IS_CORS, { err }, 500); 27 | }); 28 | }; 29 | -------------------------------------------------------------------------------- /src/capture-charge.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const pubsubRepository = require('./pubsub-repository'), 4 | paymentProcessorRepository = require('./payment-processor'), 5 | TOPIC_ARN = process.env.TOPIC_ARN; 6 | 7 | module.exports = async function captureCharge(secretKey, charge, email, paymentProcessor = paymentProcessorRepository, pubsub = pubsubRepository) { 8 | const capturedCharge = await paymentProcessor.captureCharge(secretKey, charge); 9 | capturedCharge.email = email; 10 | await pubsub.publish(capturedCharge, TOPIC_ARN); 11 | return capturedCharge; 12 | }; 13 | -------------------------------------------------------------------------------- /src/create-charge.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const pubsubRepository = require('./pubsub-repository'), 4 | paymentProcessorRepository = require('./payment-processor'), 5 | TOPIC_ARN = process.env.TOPIC_ARN, 6 | IS_CAPTURE = process.env.IS_CAPTURE; 7 | 8 | module.exports = async function chargeCustomer(secretKey, token, email, amount, currency, description = 'Charge Description', paymentProcessor = paymentProcessorRepository, pubsub = pubsubRepository) { 9 | const createdCharge = await paymentProcessor.createCharge(secretKey, token, amount, currency, IS_CAPTURE, description); 10 | createdCharge.email = email; 11 | await pubsub.publish(createdCharge, TOPIC_ARN); 12 | return createdCharge; 13 | } 14 | -------------------------------------------------------------------------------- /src/create-refund.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const pubsubRepository = require('./pubsub-repository'), 4 | paymentProcessorRepository = require('./payment-processor'), 5 | TOPIC_ARN = process.env.TOPIC_ARN; 6 | 7 | module.exports = async function createRefund(secretKey, charge, email, paymentProcessor = paymentProcessorRepository, pubsub = pubsubRepository) { 8 | const createdRefund = await paymentProcessor.createRefund(secretKey, charge); 9 | createdRefund.email = email; 10 | await pubsub.publish(createdRefund, TOPIC_ARN); 11 | return createdRefund; 12 | }; 13 | -------------------------------------------------------------------------------- /src/payment-processor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { captureAsyncFunc } = require('./tracing-repository'), 4 | CREATE_CHARGE_MESSAGE_TRACE = 'Create Charge', 5 | CAPTURE_CHARGE_MESSAGE_TRACE = 'Capture Charge', 6 | CREATE_REFUND_MESSAGE_TRACE = 'Create Refund'; 7 | 8 | module.exports = { 9 | createCharge: async function (stripeSecretKey, token, amount, currency, isCapture, description = 'Charge Description'){ 10 | const stripe = require('stripe')(stripeSecretKey); 11 | 12 | return await captureAsyncFunc(CREATE_CHARGE_MESSAGE_TRACE, () => 13 | stripe.charges.create({ 14 | source: token, 15 | amount: amount, 16 | currency: currency, 17 | description: description, 18 | capture: isCapture == 'true' 19 | }) 20 | ); 21 | }, 22 | captureCharge: async function (stripeSecretKey, charge){ 23 | const stripe = require('stripe')(stripeSecretKey); 24 | return await captureAsyncFunc(CAPTURE_CHARGE_MESSAGE_TRACE, () => stripe.charges.capture(charge)); 25 | }, 26 | createRefund: async function (stripeSecretKey, charge) { 27 | const stripe = require('stripe')(stripeSecretKey); 28 | return await captureAsyncFunc(CREATE_REFUND_MESSAGE_TRACE, () => stripe.refunds.create({charge})); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /src/process-response.js: -------------------------------------------------------------------------------- 1 | module.exports = (isCors, body, requestedCode) => { 2 | const code = requestedCode || (body ? 200 : 204); 3 | const headers = isCors ? { 4 | 'Access-Control-Allow-Headers': 'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token', 5 | 'Access-Control-Allow-Methods': 'OPTIONS,POST', 6 | 'Access-Control-Allow-Origin': '*', 7 | 'Access-Control-Max-Age': '86400' 8 | } : {}; 9 | return { 10 | statusCode: code, 11 | body: JSON.stringify(body) || '', 12 | headers: headers 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /src/pubsub-repository.js: -------------------------------------------------------------------------------- 1 | const captureAWS = require('aws-xray-sdk').captureAWS; 2 | const AWS = captureAWS(require('aws-sdk')), 3 | sns = new AWS.SNS(), 4 | NO_DATA_REPLY = 'You must provide data to the your PubSub'; 5 | 6 | module.exports = { 7 | publish: function publish(data, topic, pubSub = sns) { 8 | if (!data) { 9 | return Promise.reject(NO_DATA_REPLY); 10 | } 11 | return pubSub.publish({ 12 | Message: JSON.stringify(data), 13 | TopicArn: topic 14 | }) 15 | .promise() 16 | .catch(err => { 17 | console.log(err); 18 | return err; 19 | }); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /src/tracing-repository.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const AWSXRay = require('aws-xray-sdk-core'); 4 | AWSXRay.capturePromise(); 5 | // Makes sure that this function is not called again. 6 | // It would wrap the promise prototype multiple times. 7 | AWSXRay.capturePromise = function () { }; 8 | function captureAsyncFunc(name, func) { 9 | return new Promise(function (resolve, reject) { 10 | AWSXRay.captureAsyncFunc(name, (segment) => { 11 | func(segment).then((result) => { 12 | segment.close(); 13 | resolve(result); 14 | }, (error) => { 15 | segment.close(error); 16 | reject(error); 17 | }); 18 | }); 19 | }); 20 | } 21 | exports.captureAsyncFunc = captureAsyncFunc; 22 | ; 23 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJhY2luZy1yZXBvc2l0b3J5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsidHJhY2luZy1yZXBvc2l0b3J5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsTUFBTSxPQUFPLEdBQUcsT0FBTyxDQUFDLG1CQUFtQixDQUFDLENBQUM7QUFFN0MsT0FBTyxDQUFDLGNBQWMsRUFBRSxDQUFBO0FBQ3hCLHFEQUFxRDtBQUNyRCxzREFBc0Q7QUFDdEQsT0FBTyxDQUFDLGNBQWMsR0FBRyxjQUFhLENBQUMsQ0FBQTtBQUV2QyxTQUFnQixnQkFBZ0IsQ0FBRSxJQUFZLEVBQUUsSUFBUztJQUN2RCxPQUFPLElBQUksT0FBTyxDQUFDLFVBQVUsT0FBTyxFQUFFLE1BQU07UUFDMUMsT0FBTyxDQUFDLGdCQUFnQixDQUFDLElBQUksRUFBRSxDQUFDLE9BQVksRUFBRSxFQUFFO1lBQzlDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxJQUFJLENBQ2hCLENBQUMsTUFBVyxFQUFFLEVBQUU7Z0JBQ2QsT0FBTyxDQUFDLEtBQUssRUFBRSxDQUFBO2dCQUNmLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQTtZQUNqQixDQUFDLEVBQ0QsQ0FBQyxLQUFVLEVBQUUsRUFBRTtnQkFDYixPQUFPLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFBO2dCQUNwQixNQUFNLENBQUMsS0FBSyxDQUFDLENBQUE7WUFDZixDQUFDLENBQ0YsQ0FBQTtRQUNILENBQUMsQ0FBQyxDQUFBO0lBQ0osQ0FBQyxDQUFDLENBQUE7QUFDSixDQUFDO0FBZkQsNENBZUM7QUFBQSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiY29uc3QgQVdTWFJheSA9IHJlcXVpcmUoJ2F3cy14cmF5LXNkay1jb3JlJyk7XG5cbkFXU1hSYXkuY2FwdHVyZVByb21pc2UoKVxuLy8gTWFrZXMgc3VyZSB0aGF0IHRoaXMgZnVuY3Rpb24gaXMgbm90IGNhbGxlZCBhZ2Fpbi5cbi8vIEl0IHdvdWxkIHdyYXAgdGhlIHByb21pc2UgcHJvdG90eXBlIG11bHRpcGxlIHRpbWVzLlxuQVdTWFJheS5jYXB0dXJlUHJvbWlzZSA9IGZ1bmN0aW9uICgpIHt9XG5cbmV4cG9ydCBmdW5jdGlvbiBjYXB0dXJlQXN5bmNGdW5jIChuYW1lOiBzdHJpbmcsIGZ1bmM6IGFueSkge1xuICByZXR1cm4gbmV3IFByb21pc2UoZnVuY3Rpb24gKHJlc29sdmUsIHJlamVjdCkge1xuICAgIEFXU1hSYXkuY2FwdHVyZUFzeW5jRnVuYyhuYW1lLCAoc2VnbWVudDogYW55KSA9PiB7XG4gICAgICBmdW5jKHNlZ21lbnQpLnRoZW4oXG4gICAgICAgIChyZXN1bHQ6IGFueSkgPT4ge1xuICAgICAgICAgIHNlZ21lbnQuY2xvc2UoKVxuICAgICAgICAgIHJlc29sdmUocmVzdWx0KVxuICAgICAgICB9LFxuICAgICAgICAoZXJyb3I6IGFueSkgPT4ge1xuICAgICAgICAgIHNlZ21lbnQuY2xvc2UoZXJyb3IpXG4gICAgICAgICAgcmVqZWN0KGVycm9yKVxuICAgICAgICB9XG4gICAgICApXG4gICAgfSlcbiAgfSlcbn07XG4iXX0= -------------------------------------------------------------------------------- /src/tracing-repository.ts: -------------------------------------------------------------------------------- 1 | const AWSXRay = require('aws-xray-sdk-core'); 2 | 3 | AWSXRay.capturePromise() 4 | // Makes sure that this function is not called again. 5 | // It would wrap the promise prototype multiple times. 6 | AWSXRay.capturePromise = function () {} 7 | 8 | export function captureAsyncFunc (name: string, func: any) { 9 | return new Promise(function (resolve, reject) { 10 | AWSXRay.captureAsyncFunc(name, (segment: any) => { 11 | func(segment).then( 12 | (result: any) => { 13 | segment.close() 14 | resolve(result) 15 | }, 16 | (error: any) => { 17 | segment.close(error) 18 | reject(error) 19 | } 20 | ) 21 | }) 22 | }) 23 | }; 24 | -------------------------------------------------------------------------------- /template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: 2010-09-09 2 | Transform: AWS::Serverless-2016-10-31 3 | 4 | Globals: 5 | Function: 6 | Runtime: nodejs14.x 7 | Timeout: 10 8 | Tracing: Active 9 | 10 | Parameters: 11 | CorsOrigin: 12 | Type: String 13 | Default: "'*'" 14 | Description: (Optional) Cross-origin resource sharing (CORS) Origin. You can specify a single origin, all "*" or leave empty and no CORS will be applied. 15 | MaxLength: 250 16 | SSMParameterPath: 17 | Type: String 18 | Default: 'lambda-stripe-charge/stripe-secret-key' 19 | Description: > 20 | This component assumes the Stripe Secret key needed to use the Stripe Charge API is stored as SecureStrings in SSM Parameter Store under the path defined by 21 | this parameter. See the component README for details. 22 | AllowedPattern: ^[0-9a-zA-Z-][0-9a-zA-Z-\/]+ 23 | ConstraintDescription: 'Must start with a slash and alphanumeric characters (exclude the starting slash)' 24 | EnableInstantCapture: 25 | Type: String 26 | AllowedValues: [true, false] 27 | Default: true 28 | Description: To immediatelly capture a payment upon charge, if true it means on charge you collect the amount, if false, you need to do a capture. 29 | Conditions: 30 | ShouldEnableCapture: !Equals [true, !Ref EnableInstantCapture] 31 | Resources: 32 | ChargeStripeApiWebhook: 33 | Type: AWS::Serverless::Api 34 | Properties: 35 | Cors: 36 | AllowHeaders: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'" 37 | AllowOrigin: !Ref CorsOrigin 38 | MaxAge: "'3600'" 39 | AllowMethods: "'OPTIONS,POST'" 40 | EndpointConfiguration: REGIONAL 41 | StageName: prod 42 | TracingEnabled: true 43 | CreateStripeCharge: 44 | Type: AWS::Serverless::Function 45 | Properties: 46 | Handler: charge.handler 47 | Policies: 48 | - SNSCrudPolicy: 49 | TopicName: !GetAtt SNSTopic.TopicName 50 | - SSMParameterReadPolicy: 51 | ParameterName: !Ref SSMParameterPath 52 | Environment: 53 | Variables: 54 | IS_CORS: IsCorsDefined 55 | CORS_ORIGIN: !Ref CorsOrigin 56 | SSM_PARAMETER_PATH: !Ref SSMParameterPath 57 | TOPIC_ARN: !Ref SNSTopic 58 | IS_CAPTURE: !Ref EnableInstantCapture 59 | Events: 60 | Api: 61 | Type: Api 62 | Properties: 63 | Path: /charge 64 | Method: POST 65 | RestApiId: !Ref ChargeStripeApiWebhook 66 | CaptureStripeCharge: 67 | Type: AWS::Serverless::Function 68 | Properties: 69 | Handler: capture.handler 70 | Policies: 71 | - SNSCrudPolicy: 72 | TopicName: !GetAtt SNSTopic.TopicName 73 | - SSMParameterReadPolicy: 74 | ParameterName: !Ref SSMParameterPath 75 | Environment: 76 | Variables: 77 | IS_CORS: IsCorsDefined 78 | CORS_ORIGIN: !Ref CorsOrigin 79 | SSM_PARAMETER_PATH: !Ref SSMParameterPath 80 | TOPIC_ARN: !Ref SNSTopic 81 | Events: 82 | Api: 83 | Type: Api 84 | Properties: 85 | Path: /capture 86 | Method: POST 87 | RestApiId: !Ref ChargeStripeApiWebhook 88 | CreateRefund: 89 | Type: AWS::Serverless::Function 90 | Properties: 91 | Handler: refund.handler 92 | Policies: 93 | - SNSCrudPolicy: 94 | TopicName: !GetAtt SNSTopic.TopicName 95 | - SSMParameterReadPolicy: 96 | ParameterName: !Ref SSMParameterPath 97 | Environment: 98 | Variables: 99 | IS_CORS: IsCorsDefined 100 | CORS_ORIGIN: !Ref CorsOrigin 101 | SSM_PARAMETER_PATH: !Ref SSMParameterPath 102 | TOPIC_ARN: !Ref SNSTopic 103 | Events: 104 | Api: 105 | Type: Api 106 | Properties: 107 | Path: /refund 108 | Method: POST 109 | RestApiId: !Ref ChargeStripeApiWebhook 110 | SNSTopic: 111 | Type: AWS::SNS::Topic 112 | Outputs: 113 | ChargeApiUrl: 114 | Value: !Sub https://${ChargeStripeApiWebhook}.execute-api.${AWS::Region}.amazonaws.com/prod/charge 115 | Description: The URL of the API Gateway you provide to Stripe as a webhook it invokes to CREATE a charge based on its token. 116 | CaptureApiUrl: 117 | Value: !Sub https://${ChargeStripeApiWebhook}.execute-api.${AWS::Region}.amazonaws.com/prod/capture 118 | Description: The URL of the API Gateway you provide to Stripe as a webhook it invokes to CAPTURE a charge based on its token. 119 | RefundApiUrl: 120 | Value: !Sub https://${ChargeStripeApiWebhook}.execute-api.${AWS::Region}.amazonaws.com/prod/refund 121 | Description: The URL of the API Gateway you provide to Stripe as a webhook it invokes to CAPTURE a charge based on its token. 122 | SNSTopic: 123 | Value: !Ref SNSTopic 124 | Description: The SNS topic your Stripe Lambda is notifying to 125 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target":"ES2018", 4 | "module": "commonjs", 5 | "lib": ["es2016", "es2017.object", "es2017.string"], 6 | "strict": true, 7 | "noImplicitAny": true, 8 | "strictNullChecks": true, 9 | "noImplicitThis": true, 10 | "alwaysStrict": true, 11 | "noUnusedLocals": true, 12 | "noUnusedParameters": true, 13 | "noImplicitReturns": true, 14 | "noFallthroughCasesInSwitch": false, 15 | "inlineSourceMap": true, 16 | "inlineSources": true, 17 | "experimentalDecorators": true, 18 | "strictPropertyInitialization":false 19 | } 20 | } 21 | 22 | --------------------------------------------------------------------------------