+1

In-App Purchase iOS

In-App-Purchase.jpg

Giới thiệu

Tại Việt Nam cũng như trên thế giới, cùng với sự phát triển nóng của thị trường Mobile thì thị trường ứng dụng của Mobile cũng trở nên nhộn nhip với hàng loạt kho ứng dụng của các ông lớn. Những AppStore, Google Play, Appstore.vn, Vimarket, Mstore, Qstore, Clever Store… đã mang lại lợi nhuận siêu khủng và sự phát triển nhanh vượt bậc cho các chủ sở hữu của nó.

Mức lợi nhuận từ ngành phát triển ứng dụng di động đến từ việc bán các ứng dụng cho người dùng (trung bình 1-5 USD) và sử dụng các ứng dụng làm công cụ quảng cáo. Theo thông lệ, lợi nhuận từ ứng dụng di động được phân phối theo tỉ lệ bình quân 70% cho người phát triển và 30% còn lại cho chủ sở hữu kho ứng dụng.

Cá nhân tôi may mắn được tham gia dự án Game Card Chrono từ những ngày đầu tiên. Đây là một ứng dụng đã được đưa lên AppStore và Google Play, nội dung chính xoay quanh các Card và các vật dụng trong Game để so sánh thứ hạng của người chơi. Lợi nhuận chính thu được từ Game chính là hệ thống thanh toán trong Game (In-App Purchase) – cung cấp chức năng đổi từ tiền thật thành một loại tiền đặc biệt trong Game để có thể mua bán các vật dụng cũng như các Card quý trong Game giúp tăng thứ hạng của người chơi.

Trong nội dung của bài viết này, tôi sẽ trình bày về sơ đồ hoạt động của hệ thống thanh toán trong ứng dụng (của Apple), đồng thời đưa ra những mặt hay và mặt dở của hệ thống này để áp dụng cho hệ thống Game Chrono.

Nội dung của bài viết xin điểm qua một số ý chính như sau:

  1. Giới thiệu sơ qua về hệ thống Game Card Chrono.
  2. Giới thiệu luồng thanh toán mà Apple cung cấp.
  3. Giới thiệu luồng thanh toán trong Chrono triển khai theo Apple.
  4. Những vấn đề phát sinh trong luồng thanh toán hiện tại.
  5. Đề xuất phương pháp cải tiến luồng thanh toán.
  6. Những hiệu quả đạt được

Sau đây tôi xin đi vào chi tiết từng phần.

Giới thiệu hệ thống Game Card Chrono

System.jpg

  • Các thành phần của hệ thống:
    • Web Server (CakePHP Framework)
    • Application Client (xCode - UIWebview)
  • Cầu nối tương tác giữa Web Server và xCode :
    • Thông qua URL.
    • VD : để Web Server thông báo update thông tin gì phía xCode, Web Server sẽ chỉ định 1 controller và action tương ứng, xCode bắt được action đó sẽ xử lý thông tin.

Như đã trình bày ở trên, lợi nhuận chính của hệ thống này chính là chức năng thanh toán trong ứng dụng nên tôi xin phép được đi sâu hơn về chức năng này vì cơ bản có thể nói Game Card cũng có một thời gian dài rất HOT bên Nhật và cũng có khá nhiều công ty Clone lại ý tưởng này làm thành những Game riêng với những nội dung riêng.

Ý tưởng thanh toán đơn giản là User sẽ phải bỏ tiền thật để mua những vật dụng trong Game, mua Gacha để quay thưởng được những Card có chỉ số mạnh giúp tăng sự hùng hậu của quân đội bên mình.

Luồng thanh toán của Apple

storekit.png

  • Apple cung cấp cho chúng ta bộ thư viện StoreKit để hỗ trợ thực hiện thanh toán giữa Server Apple và Ứng dụng của chúng ta.
  • Ngoài ra luồng thanh toán của Apple được chia thành hai mô hình để triển khai : Không có Web Server trung gianCó Web Server trung gian.

Mô hình không có Web Server

no_webserver.png

Mô hình có Web Server

include_webserver.png

  • Có 3 bên tham gia vào hoạt động thanh toán :
    • App Store (Server Apple).
    • iOS Application (Chrono Client – xCode).
    • Developer Server (Chrono Server).
  • Các bước thanh toán của sơ đồ tổng quát :
    • (1) Client (xCode) gửi yêu cầu tới Web Server nhận về danh sách ID của các sản phẩm thanh toán.
    • (2) Web Server xử lý lấy dữ liệu từ database và trả về danh sách ID của các sản phẩm cho Client.
    • (3) Client gửi yêu cầu tới App Store để lấy thông tin chi tiết của từng sản phẩm (đã được tạo trên App Store).
    • (4) App Store trả về cho Client thông tin chi tiết sản phẩm.
    • (5) Client hiển thị danh sách sản phẩm ra màn hình cho User.
    • (6) User chọn 1 sản phẩm bất kỳ để thực hiện thanh toán (Vd : sản phẩm A)
    • (7) Client (xCode) gửi yêu cầu thanh toán với sản phẩm A tới App Store.
    • (8) App Store xử lý thanh toán và trả về kết quả thanh toán cho Client.
    • (9) Client nhận thông tin thanh toán từ App Store (chính là hóa đơn thanh toán – receipt). Dữ liệu trả về là 1 JSON Object. Sau đó gửi thông tin hóa đơn lên Web Server.
    • (10) Web Server nhận thông tin hóa đơn và chuẩn bị để kiểm tra tính hợp lệ của hóa đơn.
    • (11) Web Server gửi thông tin hóa đơn lên App Store nhờ kiểm tra tính hợp lệ của hóa đơn thanh toán.
    • (12) App Store kiểm tra và gửi về 2 thông tin :
      • Hóa đơn mà Web Server vừa gửi lên cho App Store.
      • Trạng thái của hóa đơn là hợp lệ hay không hợp lệ.
    • (13) Web Server đọc thông tin trả về từ App Store và xác nhận thông tin User vừa thanh toán để tiến hành các cập nhật phía Web Server.
    • (14) Sau khi cập nhật dữ liệu xong, Web Server báo về cho Client biết kết quả thanh toán để Client có xử lý điều hướng hợp lý.

Luồng thanh toán của Game Chrono

(Được triển khai theo mô hình Có Web Server trung gian)

In-app Purchase (2).jpg

  • (1) User click truy cập vào trang Shop Home, sau đó click vào trang list các sản phẩm có thể mua (/shops/select_item/1). Dữ liệu lấy từ bảng tb_coin_masters.
  • (2) User click vào 1 sản phẩm trong trang (/shops/select_item/2/$product_id), từng sản phẩm có các ID riêng biệt. ID ở đây chính là CoinMaster.apple_product_id (ID của sản phẩm trên Apple Store).
  • (3) xCode bắt được URL và bóc tách URL để lấy được product_id sau đó gửi yêu cầu lên App Store để kiểm tra thông tin product_id
  • (4) Phía App Store xử lý và đẩy thông tin sản phẩm về cho xCode.
  • (5) xCode nhận thông tin sản phẩm (valid hoặc invalid), nếu sản phẩm có tồn tại thì xCode sẽ gửi yêu cầu thanh toán (payment request) tới App Store để thực hiện thanh toán sản phẩm.
  • (6) App Store thực hiện việc thanh toán sản phẩm và trả về cho xCode kết quả thanh toán.
  • (7) xCode nhận kết quả thanh toán :
    • Trường hợp Failed : có thể User bấm Cancel hoặc không đủ tiền thanh toán thì sẽ nhảy luôn đến bước (13).
    • Trường hợp OK : gửi kết quả thanh toán chính là hóa đơn thanh toán (dạng JSON Object) lên Web Server để kiểm tra.
  • (8) Web Server nhận thông tin hóa đơn từ xCode, tiến hành một số bước tiền xử lý dữ liệu chuẩn bị đưa lên App Store validate.
  • (9) Web Server gửi thông tin hóa đơn lên App Store nhờ kiểm tra tính hợp lệ.
  • (10) App Store nhận hóa đơn, kiểm tra và trả về Web Server thông tin gồm 2 phần :
    • Hóa đơn (JSON Object) mà Web Server vừa gửi lên Apple Store.
    • Trạng thái của hóa đơn là hợp lệ hay không hợp lệ.
  • (11) Web Server nhận thông tin kiểm tra từ Apple và bóc tách dữ liệu.
  • (12) Web Server kiểm tra trạng thái của hóa đơn có hợp lệ hay không ? Nếu hợp lệ thì kiểm tra tiếp transaction_id (nằm trong hóa đơn) đã được sử dụng chưa ? (phòng trường hợp có cách nào đó User sử dụng lại được hóa đơn) Sau đó tùy từng trường hợp mà update vào database các thông tin hợp lý (VD : cập nhật Coin cho User, lưu lịch sử mua bán) sau đó thực hiện Redirect.
  • (13) Web Server sẽ được điều hướng đến trang hợp lý sau khi kiểm tra hóa đơn
    • OK : chuyển đến trang thanh toán thành công (/shops/select_item/3)
    • Failed : chuyển đến trang thanh toán thất bại (/shops/select_item/4)
  • (14) xCode bắt được URL thành công hay thất bại và bật Pop-up phía xCode để thông báo trạng thái thanh toán cho User.

Những vấn đề phát sinh trong luồng thanh toán hiện tại

  • Việc thanh toán cần sự góp mặt của cả 3 bên : App Store, Client (xCode), Web Server. Trong bài toán này ta chia thành 2 bài toán nhỏ hơn :
    • Thanh toán mua bán bằng tiền thật giữa xCode và App Store (từ bước (5) đến bước (7)).
    • Validate thanh toán giữa Web Server và App Store, sau đó thực hiện cập nhật Database phía Web Server (từ bước (8) đến bước (13)).
  • Nhìn trên sơ đồ nhận thấy rằng các bước từ (1) đến (3) chỉ là những bước lấy thông tin sản phẩm chuẩn bị cho công việc thanh toán nên nếu phía xCode bị Crash hay Web Server không load được trang thì cũng không ảnh hưởng đến việc thanh toán mua bán. Ta chỉ xét 2 luồn thanh toán tương ứng với 2 bài toán nhỏ vừa chia.

Thanh toán giữa xCode và App Store

SK_P.png

  • Khi bất kỳ sản phẩm nào đưa lên App Store thanh toán đều được add vào paymentQueue và sau khi thanh toán xong sẽ gọi hàm finishTransaction để xóa khỏi paymentQueue.

Tương tác giữa Web Server và App Store

  • Trường hợp thanh toán thất bại (user bấm cancel hoặc không đủ tiền) thì xCode sẽ điều hướng Web Server đến trang thanh toán thất bại, sau đó hiển thị pop-up cho User và kết thúc.
  • Trường hợp thanh toán thành công, xCode sẽ điều hướng đến trang /shops/validate_receipt (sau đó không thực hiện gì tiếp). Tiếp theo Web Server liên lạc với App Store để validate và cập nhật dữ liệu. Kết quả thành công hay thất bại sẽ được điều hướng tiếp đến 1 trong 2 trang ở bước (13). Tại đây xCode bắt được URL và hiển thị Popup thông báo cho User.
    • Bước này cần chạy qua 2 action trên Web Server (tức là phải chuyển URL 2 lần).

Những lỗi tiềm ẩn trong xử lý hiện tại

  • Hàm addTransactionObserver hiện tại đang đặt không đúng vị trí và không phát huy được tác dụng của hàm này. Hàm này làm nhiệm vụ quan sát paymentQueue xem còn rỗng không, nếu không rỗng thì tiếp tục xử lý những transaction còn tồn đọng trong paymentQueue.
  • Hàm finishTransaction được gọi ngay ghi thanh toán thành công (bước (7)) dẫn tới trường hợp nếu bị lỗi ở 1 trong các bước từ (8) đến (12) thì có thể dữ liệu không được cập nhật vào Database, tuy nhiên lúc này User vẫn bị trừ tiền và vẫn có mail thông báo từ Apple.

Sơ đồ cải tiến phương pháp xử lý thanh toán

In-app Purchase generation.jpg

Ý tưởng của sơ đồ này về cơ bản như sau:

  • Gộp các bước từ (8) đến (12) trong sơ đồ cũ thành 1 hàm Services xử lý validate và cập nhật kết quả vào Database (bước (*)) => tránh việc di chuyển qua nhiều URL.
  • Ở bước (7) chỉ gọi finishTransaction và xóa khỏi paymentQueue những transaction failed (user cancel hoặc không đủ tiền) và bật popup thông báo cho User luôn sau đó tiến hành điều hướng trên Web Server => những thanh toán failed không cần xử lý validate mà chỉ cần cập nhật lịch sử vào Database.
  • Ở bước (8) nhận kết quả xử lý validate với những thanh toán thành công :
    • Trường hợp mọi việc đều diễn ra bình thường => gọi hàm finishTransaction để kết thúc transaction đang nằm trong paymentQueue.
    • Trường hợp bị lỗi nào đó khi xử lý trên Web Server dẫn tới không đến được bước (8) thì transaction vẫn tồn tại trong paymentQueue.
  • Gọi hàm addTransactionObserver khi khởi động Application để xử lý paymentQueue còn tồn đọng trước khi User bắt đầu vào chơi Game.

Hiệu quả đạt được của phương pháp

  • Từ bước (6) đến trước bước (8) nếu có bất kỳ lỗi gì xảy ra thì transaction vẫn được lưu trong paymentQueue (dù có xóa App đi cũng không xóa được paymentQueue) và việc đặt hàm addTransactionObserver đúng lúc khởi động App sẽ giải quyết được vẫn đề paymentQueue vẫn còn tồn đọng thanh toán chưa được xử lý.
  • Khi hàm addTransactionObserver hoạt động thì sẽ chạy tiếp từ bước (7) => những thanh toán đã hoàn thành vẫn sẽ được xử lý tiếp cho tới khi bước (8) kết thúc và remove khỏi paymentQueue.
  • Xây dựng được một luồng hoạt động đảm bảo đồng nhất giữa Transaction thanh toán của Apple với Transaction cập nhật dữ liệu của ứng dụng.

Tham khảo

  1. https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Introduction.html
  2. https://www.dropbox.com/s/nwyc8pxf9mcnd6c/StoreKitGuide.pdf?dl=0

All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.