How Imprint built its card issuing platform in 6 months
As customer acquisition and retention costs skyrocket, branded card programs have become an emerging need for modern brands. Imprint’s first product to market, the branded rewards card, links to consumers’ bank accounts (just like Venmo or Coinbase), and comes without any baggage associated with traditional co-branded credit cards–no credit inquiries, no interest, and absolutely no fees. As of April 2022, we successfully launched 5 branded rewards cards, and we’re excited for what’s still to come.
How did we get here? In this article, we discuss how Imprint built its card issuing platform from a backend perspective–how we built it from scratch in just 6 months by prioritizing speed in all of our decisions, including our MVP scoping, our partnerships, architecture design and development.
A snapshot of the Imprint App design
Defining our MVP: What are we building?
A minimum viable product (MVP) is a pared down version of a product that has most of its core functionalities for customers to provide feedback on. As a startup, the faster we can test our MVP in the market, the better.
Our MVP included the following main features:
- Application: Apply for a new card by providing personal information
- Account management: Login and manage account settings (phone, email, pin, faceID, etc.)
- Card management: Manage their issued branded card (access virtual card number, replace cards, etc.); Review card activities and balance
- Banking and Payment: Make payments if needed
- Rewards: Accrue and redeem rewards
Leveraging service providers
We quickly sought out solid service providers to partner with us because this greatly reduces the time required to build such a comprehensive banking system.
At Imprint, there are three critical domains relying on external service providers:
- Card issuer. We chose Stripe’s issuing product to handle user & card creation, authorization and transaction management. Stripe connects to our payment network (Visa) and is also one of Imprint’s key strategic and capital partners.
- Bank account management. Plaid is the leading platform that enables applications to connect to users’ bank accounts. We use Plaid Auth to initiate payments.
- Bank partner. Because Imprint is not a bank, we require a bank partner (in our case, First Electronic Bank) in order to process funds and earn transaction fees as an “issuing bank”.
Designing distributed backend services
Most backend engineers are familiar with microservice architecture. Although it offers many great benefits such as improved system resilience, easy scalability as team size grows, etc., it requires significant initial operation costs. Since we started with only two backend engineers, we chose to build a single monolith service coupled with a few well-defined microservices, balancing quick deployment and scalability.
Here is the first version of our microservice architecture:
API Service. A lightweight backend service that provides:
- A RESTful interface to the public
- Request routing to internal services
Passport Service. An authentication service that handles:
- User authorization through OTP, pin, and password
- Request authentication through JWT
Ledger Service. As a card issuer, the most important thing is to handle transactions accurately. Ledger Service reads and writes all transactions, as well as transaction derivatives such as balance, statements, and more. It acts as a data access layer with minimal business logic in it.
At Imprint, we use Amazon QLDB as our primary source of truth for data ledger storage because it provides:
- Full support on rich compliance programs
- Serverless architecture for less operational support
- Immutable change history for auditing
Messenger Service. Most service providers communicate with their customers via emails, text messages and push notifications. Abstracting all our communication requirements into a single service reduces duplicate efforts of handling messages throughout the system.
With a message queue like Amazon SQS in place, Messenger Service is able to asynchronously process thousands of messages per second.
Banking Service. The monolith service that contains all the business logic required for the MVP, including but not limited to:
- User signup and application approvals
- Card management
- Bank account linking
- Payment & fund withdrawal
- User activity history, balance, and statementing
- Transaction authorization with stripe webhooks
- Help center
As this monolith service grows in size and complexity, and as we add more team members, it can sometimes become more difficult to manage and make changes quickly. In the future, we will split Banking Service into microservices to reduce our dependence on this single point of failure and increase our development flexibility.
Accelerating our development pace
Because our team is made up of engineers with different levels of experience, maintaining a code base that empowers everybody to contribute with speed and quality is critical.
We implemented three strategies to achieve this:
- Service communication framework
- Boilerplate code auto generation
- Component design
This year, we hired multiple new graduates and junior engineers, and they have been able to implement with similar velocity as more senior engineers.
Service communication framework. As we chose to use REST for external and gRPC for internal service communications, we were originally looking for a framework that could support both and abstract the transportation layers. In golang, there are many options, and we eventually chose GoKit because it is widely adopted. In addition, we are able to use the middleware interface provided by GoKit and easily add various common functionalities to our system. With this setup, our engineers only need to focus on business logic layer changes as we illustrate below.
Boilerplate code auto generation. In addition to the servicing framework, Imprint made several customizations on top of GoKit:
- Use customized dependencies such as logger, stats, tracing, etc.
- More abstractions needed on the business logic layer, such as DTO conversion, common data access layer, etc.
By leveraging Go Templates, all the boilerplates can be easily abstracted to save significant amounts of engineer development time.
Modularized component design. When building a monolith like Banking service, a modularized component design can be helpful to maintain a cleaner code structure. A component is a package of code that serves similar functionalities. For example, an ApplicationDao package specifically does all the CRUD operations on the application database.
The diagram below is a snapshot of how we structured Banking Service’s code. Each block is a component with multiple functions.
The benefits of this design are:
- Easy to write unit test. With modularized components, we naturally avoided mega functions (100+ lines) in our code base, and made code very easy to write unit tests by mocking the dependency components. We believe good unit tests with high coverage could reduce the manual testing effort substantially, and improve the code quality, which furtherly reduced the maintenance cost.
- Reduced engineer onboarding time. With such a clean structure and unidirectional logic flow, new engineers with little context are able to navigate the code base and start contributing quickly.
- Parallelization. The code structure naturally enables different engineers to work on the same API simultaneously. For example, to add a CreatePayment API, three engineers can work on three sub-tasks in parallel: (1) Handler layer implementation to provide API interface, request validation, and translations; (2) Manager layer implementation to integrate with dependencies; (3) Data layer implementation to provide CRUD operations in the database
- Easy to split into separate services. For example, to separate payment related business logic as a distinct payment service, we can simply copy the payment handler, manager, and DAO code, and paste it into the new service with a few minor modifications.
Some final words
At Imprint, we value speed and quality equally. Going fast doesn’t mean sacrificing development quality. Speed empowers us to go to market faster to find better solutions for our brand partners and cardholders. Focus on quality supports our speed by preventing us from getting slowed down by maintenance and potential incidents. If you are interested in discussing our approach, or want to learn more about building cool fintech products with us, please find us at talent@imprint.co or imprint.co/careers.