Hiện các app store như Apple đã giới hạn tính năng thanh toán bắt buộc lập trình viên phải sử dụng tính năng In-app purchase (IAP) trong một số trường hợp nhất định:
👉️ Subscription, mở khóa các tính năng premium, mua tiền trong game hoặc mua tính năng premium, ... [reference 1]
**Chủ yếu là để Apple ăn chia tiền kiếm được từ App
👉️ Subscription, mở khóa các tính năng premium, mua tiền trong game hoặc mua tính năng premium, ... [reference 1]
**Chủ yếu là để Apple ăn chia tiền kiếm được từ App
1. Nói qua về cách hoạt động IAP apple
Trước khi làm tính năng IAP thì ta cần 1 tài khoản apple và set up 1 app cho IAP.
Trong app này ta sẽ tạo các group trả phí, các sản phẩm (product) để bán.
Tùy theo từng trường hợp config khác nhau thì phía apple app sẽ có cách xử lý khác nhau. Thông thường dev phía mobile app sẽ chịu trách nhiệm tạo app này. Tuy nhiên sẽ có một vài trường hợp ta cần sử dụng một vài giá trị được gen bởi apple (VD: original_transaction_id) thì phải làm việc với phía mobile để config app cho đúng.
Sau khi config app mỗi lần người dùng mua một nội dung apple sẽ tạo thêm 1 transaction và bỏ transaction này vào receipt. Phía mobile app có thể đẩy receipt này lên server để server verify. Nếu receipt valid thì server sẽ unlock các premium content.
Trong app này ta sẽ tạo các group trả phí, các sản phẩm (product) để bán.
Tùy theo từng trường hợp config khác nhau thì phía apple app sẽ có cách xử lý khác nhau. Thông thường dev phía mobile app sẽ chịu trách nhiệm tạo app này. Tuy nhiên sẽ có một vài trường hợp ta cần sử dụng một vài giá trị được gen bởi apple (VD: original_transaction_id) thì phải làm việc với phía mobile để config app cho đúng.
Sau khi config app mỗi lần người dùng mua một nội dung apple sẽ tạo thêm 1 transaction và bỏ transaction này vào receipt. Phía mobile app có thể đẩy receipt này lên server để server verify. Nếu receipt valid thì server sẽ unlock các premium content.
2. Những việc cần xử lý ở server side
Tùy theo từng app cụ thể mà có thể thêm hoặc giảm bớt xử lý trên server, tuy nhiên các xử lý sẽ gồm những bước như sau:
- Trước tiên phải tạo các bảng để lưu lại thông tin về các subscription / purchase để có thể check expired_date và phục vụ cho tính năng renew.
Purchases sẽ lưu lại các giao dịch mới của người dùng. Trong trường hợp subscription được renew hay upgrade downgrade purchase sẽ không tạo bản ghi mới. Các attribute trong bảng purchase gồm có:
- start_date: ngày bắt đầu hoặc ngày người dùng mua content
- expired_date: ngày mà purchase hết hạn (dùng khi người dùng mua subscription hoặc các vật phẩm chỉ có thời hạn nhất định)
- original_transaction_id: Một id được generate bởi phía Apple, original_transaction_id sẽ thay đổi khi người dùng mua các vật phẩm / subscription khác nhau và sẽ không đổi khi người dùng downgrade và upgrade hay renew subscription. Mình sẽ dùng trường này để phân biệt renew transaction là cho purchase nào.
- user_id: id của người dùng thực hiện vụ mua bán này 😁
purchase_products sẽ lưu lại những sản phẩm mà người dùng mua cho lần purchase đó. Nó là bảng trung gian và sẽ có 2 quan hệ: quan hệ 1 là quan hệ với bảng purchases và quan hệ 2 là quan hệ polymorphic nó có thể gán với nhiều bảng sản phẩm khác nhau hoặc gán với các bảng như người dùng ( để cho trường hợp subscribe active người dùng).
- Để xử lý IAP ta cần xử lý 4 trường hợp cơ bản:
1. Purchase / Subscription mới
2. Renew purchase subscription
3. Upgrade downgrade subscription
4. Expire subscription
Ảnh minh họa:

2.1. Purchase / subscription mới.
Ta cần 1 api cho người dùng mua subscription mới api này sẽ nhận vào 2 params
- receipt mà bên mobile nhận được từ apple sau khi người dùng thanh toán thành công.
- 1 list các vật phẩm mà người dùng muốn mua
Api này sẽ sử dụng receipt và call tới server của apple, apple sẽ trả về object với list transaction ta sẽ check expire date và validate xem người dùng đã trả tiền cho vật phẩm chưa [reference 2]
Nếu receipt valid thì có thể tạo 1 bản ghi trong bảng purchase, purchase_products và tiến hành mở khóa / active các vật phẩm người dùng đã mua.
2.2. Renew purchase subscription
Đối với subscription sau mỗi tháng / tuần hay một khoảng thời gian nhất định apple sẽ tự động thanh toán khi tới kì hạn.
Sau khi thanh toán apple sẽ thêm 1 transaction vào trong receipt. Tuy nhiên giống như đã đề cập ở trên transaction mới sẽ có original_transaction_id giống với transaction purchase lần đầu.
Ta tạo 1 api mới sẽ gọi ở màn dashboard của app và được call mỗi khi mở app.
Api này chỉ nhận vào receipt. Api này cũng call tới api của apple để lấy về list transaction sau đó nó tìm các transaction có expires_date lớn nhất theo từng original_transaction_id và check xem các transaction này đã hết hạn chưa. Nếu transaction đã được renew thì cập nhật expires_date vào trong bảng purchases và active các vật phẩm. Nếu đã hết hạn thì hủy active / khóa các vật phẩm người dùng đã mua (purchase_products). Tuy nhiên cách này không đảm bảo được các content hết hạn đúng ngày expires_date cách làm đúng hơn sẽ được nói vào phần 2.4.
2.3. Upgrade downgrade subscription
Mình không dùng api mới nữa mà sẽ dùng cùng api bên 2.1. Kiểm tra xem original_transaction_id có tồn tại trong db hay không, nếu không nó sẽ là trường hợp tạo mới và xử lý như 2.1 còn nếu không thì sẽ là trường hợp update purchases và purchase_products.
Xử lý verify tương như như 2.1 sau đó update các purchase_products rồi active bọn nó thôi.
2.4. Expire subscription
Cái này mình phải tạo 1 cronjob chạy vào 1 giờ mỗi ngày (12h cũng được) job này sẽ check xem có purchases nào hết hạn vào ngày hôm qua nếu có thì hủy active / khóa các vật phẩm (purchase_products) liên kết với purchase đó cho tới khi người dùng vào lại app và chứng minh được mình đã trả tiền thông qua api ở bước 2.2
- Trước tiên phải tạo các bảng để lưu lại thông tin về các subscription / purchase để có thể check expired_date và phục vụ cho tính năng renew.
Purchases sẽ lưu lại các giao dịch mới của người dùng. Trong trường hợp subscription được renew hay upgrade downgrade purchase sẽ không tạo bản ghi mới. Các attribute trong bảng purchase gồm có: - start_date: ngày bắt đầu hoặc ngày người dùng mua content
- expired_date: ngày mà purchase hết hạn (dùng khi người dùng mua subscription hoặc các vật phẩm chỉ có thời hạn nhất định)
- original_transaction_id: Một id được generate bởi phía Apple, original_transaction_id sẽ thay đổi khi người dùng mua các vật phẩm / subscription khác nhau và sẽ không đổi khi người dùng downgrade và upgrade hay renew subscription. Mình sẽ dùng trường này để phân biệt renew transaction là cho purchase nào.
- user_id: id của người dùng thực hiện vụ mua bán này 😁
purchase_products sẽ lưu lại những sản phẩm mà người dùng mua cho lần purchase đó. Nó là bảng trung gian và sẽ có 2 quan hệ: quan hệ 1 là quan hệ với bảng purchases và quan hệ 2 là quan hệ polymorphic nó có thể gán với nhiều bảng sản phẩm khác nhau hoặc gán với các bảng như người dùng ( để cho trường hợp subscribe active người dùng).
- Để xử lý IAP ta cần xử lý 4 trường hợp cơ bản:
1. Purchase / Subscription mới
2. Renew purchase subscription
3. Upgrade downgrade subscription
4. Expire subscription
Ảnh minh họa:

2.1. Purchase / subscription mới.
Ta cần 1 api cho người dùng mua subscription mới api này sẽ nhận vào 2 params
- receipt mà bên mobile nhận được từ apple sau khi người dùng thanh toán thành công.
- 1 list các vật phẩm mà người dùng muốn mua
Api này sẽ sử dụng receipt và call tới server của apple, apple sẽ trả về object với list transaction ta sẽ check expire date và validate xem người dùng đã trả tiền cho vật phẩm chưa [reference 2]
Nếu receipt valid thì có thể tạo 1 bản ghi trong bảng purchase, purchase_products và tiến hành mở khóa / active các vật phẩm người dùng đã mua.
2.2. Renew purchase subscription
Đối với subscription sau mỗi tháng / tuần hay một khoảng thời gian nhất định apple sẽ tự động thanh toán khi tới kì hạn.
Sau khi thanh toán apple sẽ thêm 1 transaction vào trong receipt. Tuy nhiên giống như đã đề cập ở trên transaction mới sẽ có original_transaction_id giống với transaction purchase lần đầu.
Ta tạo 1 api mới sẽ gọi ở màn dashboard của app và được call mỗi khi mở app.
Api này chỉ nhận vào receipt. Api này cũng call tới api của apple để lấy về list transaction sau đó nó tìm các transaction có expires_date lớn nhất theo từng original_transaction_id và check xem các transaction này đã hết hạn chưa. Nếu transaction đã được renew thì cập nhật expires_date vào trong bảng purchases và active các vật phẩm. Nếu đã hết hạn thì hủy active / khóa các vật phẩm người dùng đã mua (purchase_products). Tuy nhiên cách này không đảm bảo được các content hết hạn đúng ngày expires_date cách làm đúng hơn sẽ được nói vào phần 2.4.
2.3. Upgrade downgrade subscription
Mình không dùng api mới nữa mà sẽ dùng cùng api bên 2.1. Kiểm tra xem original_transaction_id có tồn tại trong db hay không, nếu không nó sẽ là trường hợp tạo mới và xử lý như 2.1 còn nếu không thì sẽ là trường hợp update purchases và purchase_products.
Xử lý verify tương như như 2.1 sau đó update các purchase_products rồi active bọn nó thôi.
2.4. Expire subscription
Cái này mình phải tạo 1 cronjob chạy vào 1 giờ mỗi ngày (12h cũng được) job này sẽ check xem có purchases nào hết hạn vào ngày hôm qua nếu có thì hủy active / khóa các vật phẩm (purchase_products) liên kết với purchase đó cho tới khi người dùng vào lại app và chứng minh được mình đã trả tiền thông qua api ở bước 2.2
3. Tổng kết
Có nhiều trường hợp có thể xảy ra tùy theo app của người dùng nên không thể ốp cố định 1 cách cho tất cả các xử lý IAP mà sẽ phải thay đổi tùy theo yêu cầu từng app. Vì vậy xử lý các trường hợp này có thể làm code phức tạp hơn những gì mình mô tả ở trên.
Việc config apple app là rất quan trọng nên sẽ cần 1 mobile dev có kinh nghiệm và bàn bạc nhiều với bên BE.
Việc config apple app là rất quan trọng nên sẽ cần 1 mobile dev có kinh nghiệm và bàn bạc nhiều với bên BE.