Libgdx ios应用程序内购买

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Libgdx ios应用程序内购买相关的知识,希望对你有一定的参考价值。

我目前正在尝试在ios应用上测试应用内购买,但我不确定我是否已正确完成实施,因为当我在iOS设备上测试时,只要我点击购买产品,应用就会崩溃。这是我的代码:

store.Java

final MyGame game;
final Asset asset;
OrthographicCamera camera;
Vector3 touchPos;
Texture green_heart,cyan_heart,purple_heart,doublefuel,restore;
int life;
long itemTime;
Preferences prefs;
public static final int APPSTORE_UNDEFINED = 0;
public static final int APPSTORE_GOOGLE = 1;
public static final int APPSTORE_OUYA = 2;
public static final int APPSTORE_AMAZON = 3;
public static final int APPSTORE_DESKTOP = 4;
public static int isAppStore = APPSTORE_UNDEFINED;
public final static String cyan_1 = "cyanheart"; //cyanheart
public final static String green_2 = "greenheart"; //greenheart
public final static String purple_3 = "purpleheart"; //purpleheart
public final static String upgrade_fuel = "doublefuel"; //doublefuel
static PlatformResolver m_platformResolver;
public PurchaseManagerConfig purchaseManagerConfig;

public PurchaseObserver purchaseObserver = new PurchaseObserver() {
    @Override
    public void handleRestore (Transaction[] transactions) {
        for (int i = 0; i < transactions.length; i++) {
            if (checkTransaction(transactions[i].getIdentifier(), true) == true) break;
        }
    }
    @Override
    public void handleRestoreError (Throwable e) {
        throw new GdxRuntimeException(e);
    }
    @Override
    public void handleInstall () {  }

    @Override
    public void handleInstallError (Throwable e) {
        Gdx.app.log("ERROR", "PurchaseObserver: handleInstallError!: " + e.getMessage());
        throw new GdxRuntimeException(e);
    }
    @Override
    public void handlePurchase (Transaction transaction) {
        checkTransaction(transaction.getIdentifier(), false);
    }
    @Override
    public void handlePurchaseError (Throwable e) { //--- Amazon IAP: this will be called for cancelled
        throw new GdxRuntimeException(e);
    }
    @Override
    public void handlePurchaseCanceled () { //--- will not be called by amazonIAP
    }
};

protected boolean checkTransaction (String ID, boolean isRestore) {
    boolean returnbool = false;

    if (cyan_1.equals(ID)) {
        life += 1;
        prefs.putInteger("LIFE",life);
        prefs.flush();
        //----- put your logic for full version here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        returnbool = true;
    }
    if (green_2.equals(ID)) {
        life += 3;
        prefs.putInteger("LIFE",life);
        prefs.flush();
        //----- put your logic for full version here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        returnbool = true;
    }
    if (purple_3.equals(ID)) {
        life += 10;
        prefs.putInteger("LIFE",life);
        prefs.flush();
        //----- put your logic for full version here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        returnbool = true;
    }
    if (upgrade_fuel.equals(ID)) {
        prefs.putInteger("FUEL",100);
        prefs.flush();
        //----- put your logic for full version here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        returnbool = true;
    }
    return returnbool;
}

public Store(final MyGame game, final Asset asset){
    this.game = game;
    this.asset = asset;
    camera = new OrthographicCamera();
    camera.setToOrtho(false,625,280); //(width,height) 625,280
    touchPos = new Vector3();
    game.font3.setColor(Color.WHITE);
    game.font4.setColor(Color.GOLD);
    green_heart = asset.manager.get("greenheart.png",Texture.class);
    purple_heart = asset.manager.get("purpleheart.png",Texture.class);
    cyan_heart = asset.manager.get("cyanheart.png",Texture.class);
    doublefuel = asset.manager.get("doublefuel.png",Texture.class);
    restore = asset.manager.get("restore.png",Texture.class);
    prefs = Gdx.app.getPreferences("My Preferences");
    life = prefs.getInteger("LIFE",0);
    purchaseManagerConfig = new PurchaseManagerConfig();
    purchaseManagerConfig.addOffer(new Offer().setType(OfferType.CONSUMABLE).setIdentifier(cyan_1));
    purchaseManagerConfig.addOffer(new Offer().setType(OfferType.CONSUMABLE).setIdentifier(green_2));
    purchaseManagerConfig.addOffer(new Offer().setType(OfferType.CONSUMABLE).setIdentifier(purple_3));
    purchaseManagerConfig.addOffer(new Offer().setType(OfferType.ENTITLEMENT).setIdentifier(upgrade_fuel));
}

@Override
public void show() {
    setPlatformResolver(new AppStoreResolver(this));
    getPlatformResolver().installIAP();
}

@Override
public void render(float delta) {
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
    camera.update();
    game.batch.setProjectionMatrix(camera.combined);
    game.batch.begin();
    game.batch.draw(MainMenu.background_final, camera.position.x - (camera.viewportWidth / 2), 0);
    game.font3.draw(game.batch, "BACK",4, camera.viewportHeight - 5);
    game.font.draw(game.batch, "1    LIFE",50, camera.viewportHeight/(float)1.5);
    game.font.draw(game.batch, "3    LIFE",195, camera.viewportHeight/(float)1.5);
    game.font.draw(game.batch, "10    LIFE",340, camera.viewportHeight/(float)1.5);
    game.font4.draw(game.batch, "BEST   VALUE",320, camera.viewportHeight/(float)1.35);
    game.font.draw(game.batch, "FOREVER",490, camera.viewportHeight/(float)1.23);
    game.font.draw(game.batch, "DOUBLE",495, camera.viewportHeight/(float)1.35);
    game.font.draw(game.batch, "FUEL",507, camera.viewportHeight/(float)1.5);
    game.batch.draw(cyan_heart,55,camera.viewportHeight/3);
    game.batch.draw(green_heart,200,camera.viewportHeight/3);
    game.batch.draw(purple_heart,350,camera.viewportHeight/3);
    game.batch.draw(doublefuel,500,camera.viewportHeight/3);
    game.batch.draw(restore,(camera.viewportWidth / 2 - restore.getWidth() / 2),camera.viewportHeight/16);
    game.font5.draw(game.batch, "RESTORE",271, camera.viewportHeight/5);
    if(TimeUtils.millis() - itemTime < 1000){
        game.font6.draw(game.batch, "ITEMS   RESTORED",230, camera.viewportHeight/(float) 1.1);
    }
    game.batch.end();
    if (Gdx.input.justTouched()) {
        touchPos.set(Gdx.input.getX(),Gdx.input.getY(),0);
        camera.unproject(touchPos);
        if((touchPos.x >= 4 && touchPos.x <= 55) && touchPos.y >= camera.viewportHeight - 18){
            game.setScreen(new MainMenu(game,this.asset));
            dispose();
        }
        if((touchPos.x >= 55 && touchPos.x <= 55 + cyan_heart.getWidth()) && (touchPos.y >= camera.viewportHeight/3 && touchPos.y <= camera.viewportHeight/3 + cyan_heart.getHeight())){
            getPlatformResolver().requestPurchase(cyan_1);
        }
        if((touchPos.x >= 200 && touchPos.x <= 200 + green_heart.getWidth()) && (touchPos.y >= camera.viewportHeight/3 && touchPos.y <= camera.viewportHeight/3 + green_heart.getHeight())){
            getPlatformResolver().requestPurchase(green_2);
        }
        if((touchPos.x >= 350 && touchPos.x <= 350 + purple_heart.getWidth()) && (touchPos.y >= camera.viewportHeight/3 && touchPos.y <= camera.viewportHeight/3 + purple_heart.getHeight())){
            getPlatformResolver().requestPurchase(purple_3);
        }
        if((touchPos.x >= 500 && touchPos.x <= 500 + doublefuel.getWidth()) && (touchPos.y >= camera.viewportHeight/3 && touchPos.y <= camera.viewportHeight/3 + doublefuel.getHeight())){
            getPlatformResolver().requestPurchase(upgrade_fuel);
        }
        if((touchPos.x >= (camera.viewportWidth / 2 - restore.getWidth() / 2) && touchPos.x <= (camera.viewportWidth / 2 - restore.getWidth() / 2) + restore.getWidth()) && (touchPos.y >= camera.viewportHeight/16 && touchPos.y <= camera.viewportHeight/16 + restore.getHeight())){
            itemTime = TimeUtils.millis();
            getPlatformResolver().requestPurchaseRestore();
        }
    }
}

@Override
public void resize(int width, int height) {

}

@Override
public void pause() {

}

@Override
public void resume() {

}

@Override
public void hide() {

}

@Override
public void dispose() {
    getPlatformResolver().dispose();

}

public static PlatformResolver getPlatformResolver() {
    return m_platformResolver;
}
public static void setPlatformResolver (PlatformResolver platformResolver) {
    m_platformResolver = platformResolver;
}

public int getAppStore () {
    return isAppStore;
}
public void setAppStore (int isAppStore) {
    this.isAppStore = isAppStore;
}

}

AppStore resolver.Java

import com.badlogic.gdx.pay.PurchaseManagerConfig;


public class AppStoreResolver extends PlatformResolver {

private final static String APPLEKEY  = "......";


static final int RC_REQUEST = 10001;    // (arbitrary) request code for the purchase flow

public AppStoreResolver(Store game) {
    super(game);

    PurchaseManagerConfig config = game.purchaseManagerConfig;
    config.addStoreParam(PurchaseManagerConfig.STORE_NAME_IOS_APPLE, APPLEKEY);
    initializeIAP(null, game.purchaseObserver, config);
}}

这是我在游戏中点击应用内商品时登录xcode:

gdx-pay 4 initializeIAP(): purchaseManager == null => call 
PurchaseSystem.hasManager()
IAP 4 IAP: gdx-pay successfully instantiated.
gdx-pay 4 calls PurchaseSystem.install() via reflection
[GdxPay/AppleIOS] Installing purchase observer...
[GdxPay/AppleIOS] Requesting products...
gdx-pay 4 installed manager: AppleiOS
[GdxPay/AppleIOS] 0 products successfully received
[GdxPay/AppleIOS] Invalid product received, purpleheart
[GdxPay/AppleIOS] Invalid product received, greenheart
[GdxPay/AppleIOS] Invalid product received, doublefuel
[GdxPay/AppleIOS] Invalid product received, cyanheart
[GdxPay/AppleIOS] Purchase observer successfully installed!
[GdxPay/AppleIOS] There are 0 unfinished transactions. Try to 
finish...
art I  5041 733097 /Users/rolandvigh/Migeran-
1.3/aosp/art/runtime/gc/heap.cc:2729] Background sticky concurrent 
mark sweep GC freed 46610(1434KB) AllocSpace objects, 0(0B) LOS 
objects, 58% free, 1595KB/3MB, paused 10.712ms total 124.734ms
[GdxPay/AppleIOS] Requesting product info for cyanheart
gdx-pay 4 calls purchasemanager.purchase()
[GdxPay/AppleIOS] Error purchasing product (wrong product info count 
returned: 0)!
2018-03-02 14:43:18.738 MyGame[5041:732911] *** Terminating app due 
to 
uncaught exception 'com.badlogic.gdx.utils.GdxRuntimeException', 
reason: 
'com.badlogic.gdx.utils.GdxRuntimeException: 
java.lang.RuntimeException: 
Error purchasing product (wrong product info count returned: 0)!
at com.robot1games.game.Store$1.handlePurchaseError(Store.java:70)
at com.badlogic.gdx.pay.ios.apple.PurchaseManageriOSApple$AppleProductsDelegatePurchase.productsRequestDidReceiveResponse(PurchaseManageriOSApple.java:319)
at apple.uikit.c.UIKit.UIApplicationMain(Native Method)
at com.robot1games.game.IOSMoeLauncher.main(IOSMoeLauncher.java:24)
Caused by: java.lang.RuntimeException: Error purchasing product 
(wrong product info count returned: 0)!
... 3 more'
*** First throw call stack:
(0x218af91b 0x2104ae17 0x403219f 0x4038175 0x4041619 0x403f0a4 
0x475fb7f 0x475fb6b 0x4764655 0x21871b6d 0x21870067 0x217bf229 
0x217bf015 0x22dafac9 0x25e93189 0x403f01c 0x40412d5 0x403a103 
0x403a033 0x403ab9b 0x4041619 0x403f0a4 0x3d52851 0x3da1117 0x3da0f0f 
0x3e606e5 0x3bffe73 0x3f22b25 0x3aff319 0x174a25 0x21467873)
libc++abi.dylib: terminating with uncaught exception of type 
ObjCException
(lldb) 

PS:我还需要在xcode中为IAP处理添加任何swift类,如果是的话,我该怎么做这个Libgdx项目。

答案

您的应用程序崩溃因为抛出异常 - Store.java的第70行:

throw new GdxRuntimeException(e);

您是否正确设置了所有应用内购买?以下是有关如何使用测试用户测试您的应用内购买的信息:https://support.magplus.com/hc/en-us/articles/203809008-iOS-How-to-Test-In-App-Purchases-in-Your-App

并且不需要添加任何swift类

以上是关于Libgdx ios应用程序内购买的主要内容,如果未能解决你的问题,请参考以下文章

iOS - 我们如何通过促销代码为应用内购买提供折扣?

iOS 应用内购买的促销代码

iOS 中的应用内购买测试

读书笔记iOS-应用内购买

iOS - 应用内购买 - 恢复按钮?

(iOS) 应用内购买失败?