Recently, I helped some small business (less then 20 people) integrating payment module into their systems. I consider that it worths sharing the experiences, as this also benefit the cases include: selling code on Github, selling online courses on personal websites etc.

The payment platform manages everything for you

There are some payment platforms that manage everything for you. You do not need to bother to design the frontend UIs of “placing order” page (choosing the products listed on the page) and “paying” page (filling credit card information and actually transferring the money), not to mention the backend logics behind the UIs. The platforms do all these things for you. Let us take lemon squeezy as an example(Well, I swear I did not receive ads fee from lemon squeezy, but maybe they should do so :D).

For integrating lemon squeezy into a system, all a software engineer needs to do is:

  • Set a store page on lemon squeezy, fill the page with information includes: product information and payment information, as shown below. The customers should complete all the steps regarding buying products on the store page: select products, filling payment information and transferring money.

    Trulli
    Fig.1 - An example of lemon squeezy store page
  • Add a link to the frontend page where you would like to redirect the customers to the store page of lemon squeezy

  • Add a page for customers to activate the keys recieved from lemon squeezy. After a customer complete payment on the store page, lemon squeezy will return an activation key to the customer, the customer should use the key to claim the ownership of the product on the website. On the backend of the website, when activating the key, the software engineer needs to do two things: 1) validate the key with lemon squeezy; 2) activate the key with lemon squeezy.

With such payment platform, the software engineer only need to save the activation key and the order id returned from lemon squeezy. lemon squeezy provide a orders page for the store owners to check order information, as shown below.

Trulli
Fig.1 - The orders page provided by **lemon squeezy**

Cons:

There is no perfect solution in reality. The cons of using lemon squeezy as I observed from my own experiences are:

  • User experience. Rediect to a totally seperate payment page affect user experiences regarding online purchasing in a bad way. In some countries, the page loading speed may need up to 30 seconds.

  • Trust issue. As the domain name for the lemon squeezy store page is different from the source site, it may create trust issue for the potential customers

  • Less control. High managed platform means little spaces for customization. For instance, the layout of the store page UIs are not in the control.

Type 2: Manage everything on your own

For this type of platform, a typical work flow is:

1
select products --> select payment method --> place the order to the platform via RestAPI --> the platform returns a qrcode link for the customer to pay --> nortify the website the payment has completed

in which you have everything in control, i.e. design UIs of products page and payment page, handling all the backend logics.

Pros and Cons

The cons of adopting the type of platform is that it may takes time to build it as everything is under your control. And the pros are clearly the opposite side of the cons of type 1 platform.

Comment and share

Let us start with requirements

Business Requirements

The system needs to accept subcriptions from users with certain period time. After the paid subscription expires, the subscription status in user profile should be downgraded from a subscriped user to a normal user.

Make A Choice among Two Options

MongoDB TTL Index vs Node Schedule

As introduced in one of my previous writing regarding the System Architect , the system uses MongoDB to store user data. To add the function into the system, there were two choices that immediately came to my mind: MongoDB TTL Indexes and Node Schedule

  • Node Schedule
    Node schedule runs on the server side, for example, node-schedule. But since the requirement is to change user’s subscription status after certain amount of time (relatively long, like a week, a month etc.), and it will only run once, putting such burden on the server side for such task is not worth it.

  • MongoDB TTL Indexes
    Compare to Node Schedule, MongoDB TTL Indexes run on the database side, it will not create extra jobs on the server, it is a better choice for such requirement.

MongoDB TTL Indexes

Directly create MongoDB TTL Indexes

Clearly, in my case, I need to create MongoDB TTL Indexes with give parameters after the schema is generated. And after searching for a while, I realised that in mongoose, I can only create TTL indexes when create the schema, it is not possible to create TTL indexes with give parameters when the app is running (please correct me if I am wrong here). Within mongoose, it only can introduce TTL Index within schema definition, as shown below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import mongoose from 'mongoose';
const { Schema } = mongoose;

const XXXSchema = new Schema(
{
username: {
type: String,
required: true,
unique: true,
},
},
// timestamp of the creation data of the user
{ timestamps: true }
);

XXXSchema.index(
{ createdAt: 1 },
{
expireAfterSeconds: 30,
partialFilterExpression: { username: { $eq: 'xxx' } },
}
);

Indirectly create MongoDB TTL Indexes

Luckily, there is an alternative method to save my life: using expireAt in the schema. Under the table, the expireAt generates TTL indexes in MongoDB. Below are how I did it:

  1. introduce expireAt in the schema with expires property
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import mongoose from 'mongoose'
const { Schema } = mongoose

const XXXSchema = new Schema(
{
username: {
type: String,
required: true,
unique: true,
},
expireAt: {
type: Date,
default: Date.now,
expires: 0,
},
},
// timestamp of the creation data of the user
{ timestamps: true },
)
...
  1. when receiving the parameters, create the data with expireAt is set.
1
2
3
4
5
6
7
8
...
const newXXX = new XXX({
username: user.username,
expireAt: Date.now() + duration * 1000,
});

await newXXX.save();
...
  1. After the code in step 2 is ran, in the MongoDB, a TTL index should be seem. Let us take MongoDB Atlas as an example:
    Image

Job done :)

Comment and share

  • page 1 of 1
Author's picture

Jingjie Jiang


Find a place I love the most