准备工作
1、配置好手机的环境(一般是googlePay商城和googlePay服务,必须注册一个账号登录)
2、VPN
3、申请一个googlePay的开发者账号把一些基础信息填好,配好商品D(注:商品ID只能用一次,一但注册好删除后,就无法再次注册商品ID)
4、上传一个包体获得publicKey(代码里面需要)
代码工作
1、准备好androidStudio
2、从他官网的样品中找到aidl这个文件夹存放至Main里面
3、在AndroidManifest中添加权限
<uses-permission android:name="com.android.vending.BILLING" />
4、初始化googlePay
String base64EncodedPublicKey ="xxxxxx"; mHelper = new IabHelper(this, base64EncodedPublicKey); mHelper.enableDebugLogging(true); mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() { public void onIabSetupFinished(IabResult result) { Log.d(TAG, "Setup finished."); if (!result.isSuccess()) { // Oh noes, there was a problem.2 return; } // Have we been disposed of in the meantime? If so, quit. if (mHelper == null) return; // IAB is fully set up. Now, let's get an inventory of stuff we own. Log.d(TAG, "Setup successful. Querying inventory."); try { mHelper.queryInventoryAsync(mGotInventoryListener); } catch (IabAsyncInProgressException e) { complain("Error querying inventory. Another async operation in progress."); } } });
5、其余
import com.linecorp.game.commons.android.shaded.google.gson.Gson; import com.pokeempire.lineykct.util.IabBroadcastReceiver.IabBroadcastListener; import com.pokeempire.lineykct.util.IabHelper; import com.pokeempire.lineykct.util.IabHelper.IabAsyncInProgressException; import com.pokeempire.lineykct.util.IabResult; import com.pokeempire.lineykct.util.Inventory; import com.pokeempire.lineykct.util.Purchase; import com.unity3d.player.*; import android.Manifest; import android.app.Activity; import android.app.AlertDialog; import android.bluetooth.BluetoothAdapter; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.PixelFormat; import android.os.Build; import android.os.Bundle; import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.support.v4.content.ContextCompat; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import pub.devrel.easypermissions.EasyPermissions; public class UnityPlayerActivity extends Activity implements IabBroadcastListener { static final String TAG = "JW11"; static final String SKU_PREMIUM = "premium"; static String SKU_GAS = "gas"; static final String SKU_INFINITE_GAS_MONTHLY = "infinite_gas_monthly"; static final String SKU_INFINITE_GAS_YEARLY = "infinite_gas_yearly"; String mInfiniteGasSku = ""; String mFirstChoiceSku = ""; String mSecondChoiceSku = ""; boolean mIsPremium = false; boolean mAutoRenewEnabled = false; boolean mSubscribedToInfiniteGas = false; static final int TANK_MAX = 4; protected UnityPlayer mUnityPlayer; // don't change the name of this variable; referenced from native code IabHelper mHelper; static final int RC_REQUEST = 10001; private int IsRun = 0; // Setup activity layout @Override protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState); if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){ IsRun = 0; checkPermission(); }else { getWindow().setFormat(PixelFormat.RGBX_8888); // <--- This makes xperia play happy mUnityPlayer = new UnityPlayer(this); setContentView(mUnityPlayer); mUnityPlayer.requestFocus(); String locale = mUnityPlayer.getResources().getConfiguration().locale.getCountry(); Log.d(TAG, locale+"11111111111111"); String base64EncodedPublicKey ="xxxxxxxx"; mHelper = new IabHelper(this, base64EncodedPublicKey); mHelper.enableDebugLogging(true); mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() { public void onIabSetupFinished(IabResult result) { Log.d(TAG, "Setup finished."); if (!result.isSuccess()) { // Oh noes, there was a problem.2 return; } // Have we been disposed of in the meantime? If so, quit. if (mHelper == null) return; // IAB is fully set up. Now, let's get an inventory of stuff we own. Log.d(TAG, "Setup successful. Querying inventory."); try { mHelper.queryInventoryAsync(mGotInventoryListener); } catch (IabAsyncInProgressException e) { complain("Error querying inventory. Another async operation in progress."); } } }); Log.e("unity", "初始化"); } } // 声明一个数组,用来存储所有需要动态申请的权限 String[] permissions = new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_PHONE_STATE, // Manifest.permission.CHANGE_WIFI_STATE, // Manifest.permission.ACCESS_WIFI_STATE, // Manifest.permission.ACCESS_COARSE_LOCATION, // Manifest.permission.ACCESS_FINE_LOCATION }; // 声明一个集合,在后面的代码中用来存储用户拒绝授权的权 List<String> mPermissionList = new ArrayList<>(); boolean mShowRequestPermission = true;//用户是否禁止权限 private void checkPermission() { mPermissionList.clear(); /** * 判断哪些权限未授予 * 以便必要的时候重新申请 */ for (String permission : permissions) { if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { mPermissionList.add(permission); } } /** * 判断存储委授予权限的集合是否为空 */ if (!mPermissionList.isEmpty() && Build.VERSION.SDK_INT > 24) { // 后续操作... String[] aa = new String[mPermissionList.size()]; for (int i = 0;i<mPermissionList.size();i++ ) { aa[i] = mPermissionList.get(i); } EasyPermissions.requestPermissions(this, "手机状态信息", 100,aa); } else {//未授予的权限为空,表示都授予了 // 后续操作... RunGame(); } } public String GetAndroidName(String arg){ String model = getPhoneModel(); return model; } public String getPhoneModel() { return android.os.Build.MODEL; } public void RunGame() { if(IsRun == 1) return; IsRun = 1; getWindow().setFormat(PixelFormat.RGBX_8888); // <--- This makes xperia play happy mUnityPlayer = new UnityPlayer(this); setContentView(mUnityPlayer); mUnityPlayer.requestFocus(); String locale = mUnityPlayer.getResources().getConfiguration().locale.getCountry(); String base64EncodedPublicKey ="xxxxxxxxxx"; mHelper = new IabHelper(this, base64EncodedPublicKey); mHelper.enableDebugLogging(true); mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() { public void onIabSetupFinished(IabResult result) { Log.d(TAG, "Setup finished."); if (!result.isSuccess()) { // Oh noes, there was a problem.2 return; } // Have we been disposed of in the meantime? If so, quit. if (mHelper == null) return; // IAB is fully set up. Now, let's get an inventory of stuff we own. Log.d(TAG, "Setup successful. Querying inventory."); try { mHelper.queryInventoryAsync(mGotInventoryListener); } catch (IabAsyncInProgressException e) { complain("Error querying inventory. Another async operation in progress."); } } }); Log.e("unity", "初始化"); } IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() { public void onQueryInventoryFinished(IabResult result, Inventory inventory) { Log.d(TAG, "Query inventory finished."); // Have we been disposed of in the meantime? If so, quit. if (mHelper == null) return; // Is it a failure? if (result.isFailure()) { complain("Failed to query inventory: " + result); return; } Log.d(TAG, "Query inventory was successful."); /* * Check for items we own. Notice that for each purchase, we check * the developer payload to see if it's correct! See * verifyDeveloperPayload(). */ // Do we have the premium upgrade? Purchase premiumPurchase = inventory.getPurchase(SKU_PREMIUM); mIsPremium = (premiumPurchase != null && verifyDeveloperPayload(premiumPurchase)); Log.d(TAG, "User is " + (mIsPremium ? "PREMIUM" : "NOT PREMIUM")); // First find out which subscription is auto renewing Purchase gasMonthly = inventory.getPurchase(SKU_INFINITE_GAS_MONTHLY); Purchase gasYearly = inventory.getPurchase(SKU_INFINITE_GAS_YEARLY); if (gasMonthly != null && gasMonthly.isAutoRenewing()) { mInfiniteGasSku = SKU_INFINITE_GAS_MONTHLY; mAutoRenewEnabled = true; } else if (gasYearly != null && gasYearly.isAutoRenewing()) { mInfiniteGasSku = SKU_INFINITE_GAS_YEARLY; mAutoRenewEnabled = true; } else { mInfiniteGasSku = ""; mAutoRenewEnabled = false; } // The user is subscribed if either subscription exists, even if neither is auto // renewing mSubscribedToInfiniteGas = (gasMonthly != null && verifyDeveloperPayload(gasMonthly)) || (gasYearly != null && verifyDeveloperPayload(gasYearly)); Log.d(TAG, "User " + (mSubscribedToInfiniteGas ? "HAS" : "DOES NOT HAVE") + " infinite gas subscription."); // Check for gas delivery -- if we own gas, we should fill up the tank immediately Log.d("SKU_GAS=", SKU_GAS); Purchase gasPurchase = inventory.getPurchase(SKU_GAS); if (gasPurchase != null && verifyDeveloperPayload(gasPurchase)) { Log.d(TAG, "We have gas. Consuming it."); try { mHelper.consumeAsync(inventory.getPurchase(SKU_GAS), mConsumeFinishedListener); } catch (IabAsyncInProgressException e) { complain("Error consuming gas. Another async operation in progress."); } return; }else { Log.d("SKU_GAS=", SKU_GAS); } Log.d(TAG, "Initial inventory query finished; enabling main UI."); } }; IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() { public void onConsumeFinished(Purchase purchase, IabResult result) { Log.d(TAG, "Consumption finished. Purchase: " + purchase + ", result: " + result); // if we were disposed of in the meantime, quit. if (mHelper == null) return; // We know this is the "gas" sku because it's the only one we consume, // so we don't check which sku was consumed. If you have more than one // sku, you probably should check... if (result.isSuccess()) { // successfully consumed, so we apply the effects of the item in our // game world's logic, which in our case means filling the gas tank a bit // game world's logic, which in our case means filling the gas tank a bit Log.d(TAG, "Consumption successful. Provisioning."); Map<String, Object> map = new HashMap<String, Object>(); map.put("signtureData", purchase.getOriginalJson()); map.put("signture",purchase.getSignature()); String json = new Gson().toJson(map); mUnityPlayer.UnitySendMessage("Main","PostGooglePay",json); } else { complain("Error while consuming: " + result); } Log.d(TAG, "End consumption flow."); } }; public void LineGooglePay(String id,String orderId ){ Log.i(TAG,"支付调起"); Log.d(TAG, "支付调起"+id); SKU_GAS=id; try { mHelper.launchPurchaseFlow(this, id, RC_REQUEST, mPurchaseFinishedListener, orderId); } catch (IabAsyncInProgressException e) { complain("Error launching purchase flow. Another async operation in progress."); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data); if (mHelper == null) return; // Pass on the activity result to the helper for handling if (!mHelper.handleActivityResult(requestCode, resultCode, data)) { // not handled, so handle it ourselves (here's where you'd // perform any handling of activity results not related to in-app // billing... super.onActivityResult(requestCode, resultCode, data); } else { Log.d(TAG, "onActivityResult handled by IABUtil."); } } IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() { public void onIabPurchaseFinished(IabResult result, Purchase purchase) { Log.d(TAG, "Purchase finished: " + result + ", purchase: " + purchase); // if we were disposed of in the meantime, quit. if (mHelper == null) return; if (result.isFailure()) { complain("Error purchasing: " + result); return; } if (!verifyDeveloperPayload(purchase)) { complain("Error purchasing. Authenticity verification failed."); return; } Log.d(TAG, "Purchase successful."); try { mHelper.consumeAsync(purchase, mConsumeFinishedListener); } catch (IabAsyncInProgressException e) { complain("Error consuming gas. Another async operation in progress."); return; } } }; @Override public void receivedBroadcast() { // Received a broadcast notification that the inventory of items has changed Log.d(TAG, "Received broadcast notification. Querying inventory."); try { mHelper.queryInventoryAsync(mGotInventoryListener); } catch (IabAsyncInProgressException e) { complain("Error querying inventory. Another async operation in progress."); } } boolean verifyDeveloperPayload(Purchase p) { String payload = p.getDeveloperPayload(); /* * TODO: verify that the developer payload of the purchase is correct. It will be * the same one that you sent when initiating the purchase. * * WARNING: Locally generating a random string when starting a purchase and * verifying it here might seem like a good approach, but this will fail in the * case where the user purchases an item on one device and then uses your app on * a different device, because on the other device you will not have access to the * random string you originally generated. * * So a good developer payload has these characteristics: * * 1. If two different users purchase an item, the payload is different between them, * so that one user's purchase can't be replayed to another user. * * 2. The payload must be such that you can verify it even when the app wasn't the * one who initiated the purchase flow (so that items purchased by the user on * one device work on other devices owned by the user). * * Using your own server to store and verify developer payloads across app * installations is recommended. */ return true; } void complain(String message) { //Log.e(TAG, "**** TrivialDrive Error: " + message); //alert("Error: " + message); } void alert(String message) { AlertDialog.Builder bld = new AlertDialog.Builder(this); bld.setMessage(message); bld.setNeutralButton("OK", null); Log.d(TAG, "Showing alert dialog: " + message); bld.create().show(); } // Quit Unity @Override protected void onDestroy () { mUnityPlayer.quit(); super.onDestroy(); if (mHelper != null) { try { mHelper.dispose(); } catch (IabAsyncInProgressException e) { e.printStackTrace(); } } mHelper = null; } // Pause Unity @Override protected void onPause() { super.onPause(); mUnityPlayer.pause(); } // Resume Unity @Override protected void onResume() { super.onResume(); mUnityPlayer.resume(); } // This ensures the layout will be correct. @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); mUnityPlayer.configurationChanged(newConfig); } // Notify Unity of the focus change. @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); mUnityPlayer.windowFocusChanged(hasFocus); } // For some reason the multiple keyevent type is not supported by the ndk. // Force event injection by overriding dispatchKeyEvent(). @Override public boolean dispatchKeyEvent(KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_MULTIPLE) return mUnityPlayer.injectEvent(event); return super.dispatchKeyEvent(event); } // Pass any events not handled by (unfocused) views straight to UnityPlayer @Override public boolean onKeyUp(int keyCode, KeyEvent event) { return mUnityPlayer.injectEvent(event); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { return mUnityPlayer.injectEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { return mUnityPlayer.injectEvent(event); } /*API12*/ public boolean onGenericMotionEvent(MotionEvent event) { return mUnityPlayer.injectEvent(event); } }