Plugin

IAP

Mua hàng trong game với Google Play Billing 9 (Android) và StoreKit (iOS) — cùng một API, kết nối store tự chạy khi app mở.

v1.0.0 Android · Billing 9.1 iOS · StoreKit Autoload IAP Addon leono_iap
Tải addon (.zip) Hướng dẫn cài

Gồm thư viện Android. Thư viện iOS chưa có trong gói (cần build trên Mac) — trên iOS các hàm trả kết quả rỗng. Giải nén vào thư mục gốc project Godot.

Tạo sản phẩm trên store

  1. Play Console → app → Monetize with Play → Products.
  2. In-app products: gem, coin (tiêu hao) và remove ads (mua một lần). Ví dụ ID: gem_100, remove_ads.
  3. Subscriptions: gói VIP, mỗi gói cần ít nhất một base plan đang active.
  4. App phải được upload ít nhất lên Internal testing thì Billing mới trả sản phẩm.
  5. Test không mất tiền: Settings → License testing → thêm Gmail người test.
  1. App Store Connect → app → Monetization → In-App Purchases / Subscriptions. Nên dùng cùng Product ID với Android.
  2. Bật capability In-App Purchase cho App ID.
  3. Test bằng tài khoản Sandbox (Users and Access → Sandbox).

Cài plugin

  1. Buildbuild_android.bat iap; iOS trên Mac: scripts/build_ios.sh iap.
  2. Copyplugins/iap/addon/leono_iap/ vào res://addons/.
  3. Bật pluginProject Settings → Plugins → Leono IAP. Autoload IAP tự được thêm.
  4. ExportAndroid: Use Gradle Build (permission BILLING tự thêm). iOS: export bình thường, StoreKit tự được link.

Bắt đầu

const GEMS := {"gem_100": 100, "gem_550": 550}

func _ready() -> void:
    IAP.purchase_updated.connect(_deliver)   # nối sớm
    var products := await IAP.query_products(GEMS.keys() + ["remove_ads"], ["vip_monthly"])
    for id in products:
        shop.set_price(id, products[id].price)

func buy(id: String) -> void:
    var r := await IAP.purchase(id)
    match r.status:
        "purchased": await _deliver(r.purchase)
        "pending": show_message("Thanh toán đang chờ xử lý")
        "error": show_message("Không mua được: " + r.error)

func _deliver(p: Dictionary) -> void:
    if p.state != "purchased":
        return
    if GEMS.has(p.product_id):
        add_gems(GEMS[p.product_id])
        await IAP.consume(p)        # tiêu hao
    else:
        unlock(p.product_id)
        await IAP.acknowledge(p)    # mua một lần / subscription
Quy tắc vàng

Giao vật phẩm trước, rồi mới consume() / acknowledge(). Nếu app crash giữa chừng, purchase chưa hoàn tất sẽ được store gửi lại qua purchase_updated hoặc restore_purchases() để giao tiếp — người chơi không bị mất tiền oan.

Cấu hình

Không có Project Setting. Tuỳ chọn duy nhất là IAP.simulate_purchases để test trong editor.

IAP.simulate_purchases = not OS.has_feature("mobile")   # editor/PC: mua giả luôn thành công

API reference

is_ready()query_products() purchase()consume() acknowledge()restore_purchases() purchase_updatedPurchase
is_ready() → boolAndroidiOS

Android: đã kết nối Google Play. iOS: máy cho phép thanh toán (không bị Screen Time chặn). Các hàm khác tự chờ kết nối tối đa 5 giây nên thường không cần gọi.

query_products(inapp_ids := [], subs_ids := []) → DictionaryawaitAndroidiOS

Lấy thông tin và giá theo tiền tệ của người chơi. Phải gọi trước purchase(). ID không có trong kết quả là chưa tạo hoặc chưa active trên store.

KeyKiểuMô tả
id, title, descriptionString
typeString"inapp" / "subs"
priceStringĐã định dạng, ví dụ "25.000 ₫" — hiển thị thẳng
price_micros, currencyint, StringGiá × 1.000.000 và mã tiền tệ — dùng cho analytics
billing_periodStringChỉ subscription, ISO 8601: P1W, P1M, P1Y
purchase(product_id: String) → DictionaryawaitAndroidiOS

Mở hộp thoại thanh toán của store. Trả {status, purchase, error}. Mỗi lúc chỉ một giao dịch.

statusÝ nghĩa
purchasedĐã thanh toán — giao vật phẩm rồi consume/acknowledge
pendingChờ thanh toán (tiền mặt ở cửa hàng, Ask to Buy) — chưa giao; kết quả đến sau qua purchase_updated
cancelledNgười chơi đóng hộp thoại
errorXem error: product_not_loaded, purchase_in_progress, already_owned, billing_unavailable, network_error, service_unavailable, item_unavailable, developer_error, not_available

already_owned (Android): người chơi đã mua nhưng game chưa consume — gọi restore_purchases() rồi giao + consume.

consume(purchase: Dictionary) → boolawaitAndroidiOS

Cho item tiêu hao (gem, coin, lượt chơi): hoàn tất giao dịch để mua lại được.

acknowledge(purchase: Dictionary) → boolawaitAndroidiOS

Cho item mua một lần (remove ads, mở khoá) và subscription. Purchase đã acknowledge rồi thì trả true ngay.

Android: purchase không được acknowledge trong 3 ngày sẽ bị Google tự hoàn tiền và thu hồi.

restore_purchases() → ArrayawaitAndroidiOS

Các purchase đang sở hữu. Android: in-app đang sở hữu (gồm cả chưa consume) + subscription active — không hiện gì, gọi lúc mở game được. iOS: non-consumable + subscription (Apple không khôi phục item tiêu hao).

iOS có thể hỏi mật khẩu Apple ID — chỉ gọi từ nút "Khôi phục giao dịch". Apple bắt buộc game bán non-consumable phải có nút này.

signal purchase_updated(purchase: Dictionary)AndroidiOS

Purchase hoàn tất ngoài lời gọi purchase(): thanh toán pending vừa xong, mua từ trang store/promo code, giao dịch chưa hoàn tất từ lần chạy trước (iOS gửi lại khi mở app). Nối signal này sớm, xử lý giống kết quả purchased.

Purchase

KeyAndroidiOS
product_idProduct ID
state"purchased" / "pending"
tokenPurchase tokenTransaction ID
order_idGPA.xxxxOriginal transaction ID
acknowledgedĐã acknowledge chưaLuôn false
quantity, timeSố lượng; thời điểm mua (ms)
signature, original_jsonĐể server xác thực""
receipt""App receipt base64 để server xác thực với Apple

Hướng dẫn: xác thực phía server

Plugin không tự xác thực giao dịch. Game có kinh tế quan trọng (PvP, giao dịch giữa người chơi) nên gửi purchase lên server và chỉ giao hàng khi server xác nhận:

Khác biệt giữa nền tảng

AndroidiOS
Kết nốiTự kết nối và tự kết nối lạiKhông cần
consume / acknowledgeKhác nhauNhư nhau (finish transaction)
restore_purchases()Không hiện gìCó thể hỏi mật khẩu Apple ID
Subscription có nhiều offerDùng base plan đầu tiênTheo sản phẩm
API storePlay Billing 9.1.0StoreKit 1 (deprecated từ iOS 18, vẫn chạy)
iOS chưa được kiểm tra

Code iOS chưa được build — cần máy Mac chạy scripts/build_ios.sh iap và test bằng tài khoản Sandbox.

Lịch sử phiên bản

Phiên bảnThay đổi
1.0.0Phát hành đầu tiên: Play Billing 9.1.0, StoreKit 1.