You are developing the backend system for a music streaming platform based on subscriptions and plans.
The AWS infrastructure is already provisioned for you. You will receive access to a dedicated AWS account containing:
- An empty Lambda function: fender_digital_code_exercise(you'll write and deploy the code)
- A DynamoDB table for data storage
- An API Gateway REST API: fender_digital_code_exercise
| Cloud architecture diagram | 
Write the Lambda function code to support two operations:
- Get subscription data for a user
- Process subscription webhook events (creation, renewal, cancellation)
Configure API Gateway to expose the following endpoints:
- GET /api/v1/subscriptions/{userId}
- POST /api/v1/webhooks/subscriptions
Deploy your code using the provided deployment scripts.
Write E2E tests to validate the subscription flow.
- Configure the API Gateway endpoints with Lambda proxy integration
- Create a deployment and stage for the REST API
- Create an API key and usage plan, and connect it to the stage
- Implement the Lambda function handler code
- Deploy your code using the provided scripts
- Test the complete subscription flow
- 
Each user can only have one active subscription at a time 
- 
The subscription statusfield must be derived from the data using the following rules:- The status is activeif thecanceledAtfield is not set
- The status is pendingif thecancelledAtfield is set, but current date is before theexpiresAtdate
- The status is cancelledif thecancelledAtfield is set, and current date is after theexpiresAtdate
 
- The status is 
- 
Subscriptions cannot be created for plans with inactivestatus
- 
planitems must be created manually in the AWS Console DynamoDB service
- 
The response of the GET /api/v1/subscriptions/{userId}should also contain the associated plan data
| Field name | Description | DynamoDB type | 
|---|---|---|
| pk | Partition key of the item (e.g. user:<userId>) | String | 
| sk | Sort key of the item (e.g. sub:<subId>}) | String | 
| type | Item type (always sub) | String | 
| planSku | SKU of the subscription plan | String | 
| startDate | ISO-8601 string of subscription start datetime | String | 
| expiresAt | ISO-8601 string of subscription expiration datetime | String | 
| canceledAt | ISO-8601 string of subscription cancelation datetime | String | 
| lastModifiedAt | ISO-8601 string of last modified datetime | String | 
| attributes  | Extra attributes for the subscription (metadata) | Map | 
| Field name | Description | DynamoDB type | 
|---|---|---|
| pk | Partition key of the item (e.g. plan:<sku>). | String | 
| sk | Sort key of the item (e.g. meta) | String | 
| type | Item type (always plan) | String | 
| name | Name of the plan | String | 
| price | Price of the plan | Number | 
| currency | Currency of the plan price | String | 
| billingCycle | Billing cycle of the plan ( monthyoryearly) | String | 
| features | List of features (as strings) | List | 
| status | Status of the plan ( activeorinactive) | String | 
| lastModifiedAt | ISO-8601 string of last modified datetime | String | 
{
  "userId": "123",
  "subscriptionId": "sub_456789",
  "plan": {
    "sku": "PREMIUM_MONTHLY",
    "name": "Premium Monthly",
    "price": 9.99,
    "currency": "USD",
    "billingCycle": "MONTHLY",
    "features": [
      "HD Streaming",
      "Offline Downloads",
      "Ad Free"
    ],
  },
  "startDate": "2024-03-20T10:00:00Z",
  "expiresAt": "2024-04-20T10:00:00Z",
  "cancelledAt": null,
  "status": "ACTIVE",
  "attributes": {
    "autoRenew": true,
    "paymentMethod": "CREDIT_CARD"
  }
}- Subscription creation event (subscription.created)
{
  "eventId": "evt_123456789",
  "eventType": "subscription.created",
  "timestamp": "2024-03-20T10:00:00Z",
  "provider": "STRIPE",
  "subscriptionId": "sub_456789",
  "paymentId": "pm_123456",
  "userId": "123",
  "customerId": "cus_789012",
  "expiresAt": "2024-04-20T10:00:00Z",
  "metadata": {
    "planSku": "PREMIUM_MONTHLY",
    "autoRenew": true,
    "paymentMethod": "CREDIT_CARD"
  }
}- Subscription renewal event (subscription.renewed)
{
  "eventId": "evt_987654321",
  "eventType": "subscription.renewed",
  "timestamp": "2024-04-20T10:00:00Z",
  "provider": "STRIPE",
  "subscriptionId": "sub_456789",
  "paymentId": "pm_654321",
  "userId": "123",
  "customerId": "cus_789012",
  "expiresAt": "2024-05-20T10:00:00Z",
  "metadata": {
    "planSku": "PREMIUM_MONTHLY",
    "autoRenew": true,
    "paymentMethod": "CREDIT_CARD"
  }
}- Subscription cancelation event (subscription.cancelled)
{
  "eventId": "evt_456789123",
  "eventType": "subscription.cancelled",
  "timestamp": "2024-05-20T10:00:00Z",
  "provider": "STRIPE",
  "subscriptionId": "sub_456789",
  "paymentId": null,
  "userId": "123",
  "customerId": "cus_789012",
  "expiresAt": "2024-05-20T10:00:00Z",
  "cancelledAt": "2024-05-20T10:00:00Z",
  "metadata": {
    "planSku": "PREMIUM_MONTHLY",
    "autoRenew": false,
    "paymentMethod": "CREDIT_CARD",
    "cancelReason": "USER_REQUESTED"
  }
}- A Unix-based OS (Linux distro, MacOS or WSL2)
- AWS CLI v2 (installation guide)
To set up your local environment, start by creating a fork of this repository and cloning that into your local machine.
Some steps in the coding exercise process require interaction with AWS through the AWS CLI. You will need to create a new profile called fender. You can do so by running the following command and entering the variables.
aws configure --profile fenderAWS Access Key ID: <your access key id>
AWS Secret Access Key: <your secret access key>
Default region name: us-east-1
Default output format: json
To ensure correct configuration, run the following command
aws lambda list-functions --profile fenderYou should see a function called fender_digital_code_exercise
To manage environment variables, create a .env file in the root directory of the repository. This file will be used to sync the Lambda runtime environment variables when deployed.
The .env file MUST follow the traditional convention of KEY=VALUE in order for the deployment to work. Here's an example:
VARIABLE_ONE=Hello
VARIABLE_TWO=World!
# Comments and newlines are allowed
ANOTHER_ONE=foo
LAST_ONE=barWe provide detailed instructions on development for the following languages.
If you want to use a different language, create a new folder in the app directory and manage it however you want.
The first step is to deploy any environment variables to the Lambda runtime.
Make sure you have a .env file in the repository root and run the following command.
make deploy-envThis will take all of the variables in the .env file you created and add them to the Lambda runtime.
If you used one of the supported languages, run one of the following commands to deploy your code. These will only work if the development instructions for each language were followed and the AWS CLI was correctly set up.
make deploy-node    # For Node.js runtime
make deploy-python  # For Python runtime
make deploy-go      # For Go runtimeIf you decided to use a different language, you will have to manually configure and deploy the Lambda function.
To test your integration, create and configure an API Gateway stage, and call the Invoke URL with the desired path.
An E2E test should be created and should do the following operations.
- Create a new test plan (done manually)
- Call the POST /api/v1/webhooks/subscriptionsendpoint to create a new subscription
- Call the GET /api/v1/subscriptions/{userId}endpoint to retrieve data for the newly created subscription
- Call the POST /api/v1/webhooks/subscriptionsendpoint to renew the subscription
- Call the GET /api/v1/subscriptions/{userId}endpoint to verify the renewal
- Call the POST /api/v1/webhooks/subscriptionsendpoint to cancel the subscription
- Call the GET /api/v1/subscriptions/{userId}endpoint to verify the cancelation
- Clean up all of the test data from the table (done manually)
You can use cURL commands or a tool like Postman to write your tests.
When the exercise is complete, send a pull request from your fork to the parent repository.
The forked repository should contain your Lambda source code somewhere in the app folder.