package com.uiap; import java.io.IOException; import java.security.InvalidKeyException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.List; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.util.Base64; import android.util.Log; import com.uiap.util.IabHelper; import com.uiap.util.IabResult; import com.uiap.util.Inventory; import com.uiap.util.Purchase; import com.uiap.util.SkuDetails; import com.unity3d.player.UnityPlayer; import com.unity3d.player.UnityPlayerActivity; public class MainActivity extends UnityPlayerActivity { IabHelper mHelper; //Google Billing String base64EncodedPublicKey; final String GOOGLE_TAG = "GoogleBilling"; List<String> m_reqSkuList = new ArrayList<String>(); public static MainActivity currentActivity = null; String mCurPurchase = ""; @Override protected void onCreate (Bundle savedInstanceState) { super.onCreate(savedInstanceState); currentActivity = this; //Get ConfigData From Manifest ActivityInfo info = null; try { info = this.getPackageManager().getActivityInfo(getComponentName(),PackageManager.GET_META_DATA); } catch (NameNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } String key=info.metaData.getString("PublicKey"); base64EncodedPublicKey = key; String defaultSku=info.metaData.getString("defaultSku"); String[] skus = defaultSku.split(","); m_reqSkuList = new ArrayList<String>(); for(int i = 0 ;i<skus.length;++i) { m_reqSkuList.add(skus[i]); } initGoogleBill(); } //------------Google Billing begin---------- //init Google Billing and query unFinished order void initGoogleBill() { // compute your public key and store it in base64EncodedPublicKey mHelper = new IabHelper(this, base64EncodedPublicKey); mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() { public void onIabSetupFinished(IabResult result) { if (!result.isSuccess()) { // Oh noes, there was a probl em. Log.d(GOOGLE_TAG, "Problem setting up In-app Billing: " + result); } // Hooray, IAB is fully set up! Log.d(GOOGLE_TAG, "Hooray, IAB is fully set up!"); /* * Although we should call getList() when we init game, but If we are not, and call pay() directly, * ,it will case an Error.so I add this to make sure it work. * If you want control unfinished order checking by your self, * you can delete under code,and choose right time to call getList() */ mHelper.queryInventoryAsync(mQueryItems); } }); } final IabHelper.QueryInventoryFinishedListener mQueryItems = new IabHelper.QueryInventoryFinishedListener() { public void onQueryInventoryFinished(IabResult result, Inventory inventory) { if (result.isFailure()) { // handle error return; } //put all items of inventory which is in skuList to JSONObject JSONObject jsonResult = new JSONObject(); try { for(int i = 0; i<m_reqSkuList.size();++i) { String sku = m_reqSkuList.get(i); SkuDetails detail = inventory.getSkuDetails(sku); JSONObject info = fillInfo(detail); jsonResult.put(sku, info); } } catch (JSONException ex) { throw new RuntimeException(ex); } String resultString = jsonResult.toString(); Log.d(GOOGLE_TAG,"Inventory Result: "+ resultString); //responds the itemList data to Unity3d if(jsonResult.length() >0) { UnityPlayer.UnitySendMessage("Bridge", "onGetItem", jsonResult.toString()); } Log.d(GOOGLE_TAG, "check unfinished ..."); //check unfinished purchase in skuList for(int i = 0; i<m_reqSkuList.size();++i) { String sku = m_reqSkuList.get(i); Purchase pur = inventory.getPurchase(sku); if (pur != null && verifyDeveloperPayload(pur)) { Log.d(GOOGLE_TAG, "find unfinished purchase, SKU: "+sku); mCurPurchase = sku; mHelper.consumeAsync(pur,mConsumeFinishedListener); return; } } } }; public void getItems(String jsonStr) { try { if(jsonStr.length() > 0) { JSONObject json = new JSONObject(jsonStr); JSONArray list = json.getJSONArray("items"); m_reqSkuList = new ArrayList<String>(); for(int i = 0 ;i<list.length();++i) { m_reqSkuList.add(list.getString(i)); } } new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { mHelper.queryInventoryAsync(true, m_reqSkuList, mQueryItems); } }); } catch (JSONException ex) { throw new RuntimeException(ex); } } public void payWithPayLoad(String itemSKU,String payLoad) { mCurPurchase = itemSKU; mHelper.launchPurchaseFlow(this, itemSKU, 10001, mPurchaseFinishedListener,payLoad); } IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() { public void onIabPurchaseFinished(IabResult result, Purchase purchase) { Log.d(GOOGLE_TAG, "Purchase finished: " + result + ", purchase: " + purchase); if (result.isFailure()) { Log.d(GOOGLE_TAG, "Error purchasing: " + result); return; } if (!verifyDeveloperPayload(purchase)) { Log.d(GOOGLE_TAG, "Error purchasing. Authenticity verification failed."); return; } Log.d(GOOGLE_TAG, "Purchase successful."); /* * if your item is consume,you MUST call this. * In Google Billing,everything isn't consumed until you call consumeAsync. * Because nearly all IAP is consumed,I didn't make a data to save item kind */ if (purchase.getSku().equals(mCurPurchase)) { // remove query inventory method from here and put consumeAsync() directly mHelper.consumeAsync(purchase, mConsumeFinishedListener); } JSONObject data = new JSONObject(); try { data.put("signature",purchase.getSignature()); data.put("purchase",purchase.toString()); } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } //responds Unity3d purchase result UnityPlayer.UnitySendMessage("Bridge", "onPay", data.toString()); } }; IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() { public void onConsumeFinished(Purchase purchase, IabResult result) { if (result.isSuccess()) { // provision the in-app purchase to the user // (for example, credit 50 gold coins to player's character) Log.d(GOOGLE_TAG,"Consume "+ mCurPurchase+" Success"); } else { // handle error Log.d(GOOGLE_TAG,"Consume "+ mCurPurchase+" Error"); } } }; JSONObject fillInfo(SkuDetails detail) { JSONObject data = null; try { if(detail != null) { String price = detail.getPrice(); String desc = detail.getDescription(); String title = detail.getTitle(); String product = detail.getSku(); data = new JSONObject(); data.put("title", title); data.put("price", price); data.put("desc", desc); data.put("product", product); } } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } return data; } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { Log.d(GOOGLE_TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data); // 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(GOOGLE_TAG, "onActivityResult handled by IABUtil."); } } /** Verifies the developer payload of a purchase. */ 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. */ boolean result = false; try { String info = p.getOriginalJson().toString(); String sign = p.getSignature(); byte[] keyBytes = Base64.decode(base64EncodedPublicKey,Base64.DEFAULT); X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PublicKey publicKey2 = keyFactory.generatePublic(x509EncodedKeySpec); Log.d(GOOGLE_TAG,publicKey2.toString()); Signature signature = Signature.getInstance("SHA1WithRSA"); signature.initVerify(publicKey2); signature.update(info.getBytes()); result =signature.verify(Base64.decode(sign.getBytes(),Base64.DEFAULT)); } catch (NoSuchAlgorithmException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvalidKeySpecException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvalidKeyException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SignatureException e) { // TODO Auto-generated catch block e.printStackTrace(); } Log.d(GOOGLE_TAG,"verify result is "+result); return result; } }