Android App 內構程式(二)

第二篇了,我們先設定內部測試和測試帳號,這樣子你測試才不用拿真的信用卡,很重要!!


接著,要開始APP的部分:

首先,在build.gradle加上這行
implementation 'com.android.billingclient:billing:3.0.1'
AndroidManifest.xml 加入:
<uses-permission android:name="com.android.vending.BILLING" />
先上XML:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<
TextView
android:id="@+id/purchase_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="buy"/>
           <Button
android:id="@+id/purchase_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我要購買"
android:onClick="purchase"
/>
</
LinearLayout>

接著是buy.java
先宣告BillingClient,把你的google play 的PURCHASE_KEY和PRODUCT_ID複製
private BillingClient billingClient;
public static final String PREF_FILE= "MyPref";
public static final String PURCHASE_KEY= "key";
public static final String PRODUCT_ID= "myproduct";
connect billing client:
billingClient = BillingClient.newBuilder(this)
.enablePendingPurchases().setListener(this).build();
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(BillingResult billingResult) {
if(billingResult.getResponseCode()==BillingClient.BillingResponseCode.OK){
Purchase.PurchasesResult queryPurchase = billingClient.queryPurchases(INAPP);
List<Purchase> queryPurchases = queryPurchase.getPurchasesList();
if(queryPurchases!=null && queryPurchases.size()>0){
handlePurchases(queryPurchases);
}
//if purchase list is empty that means item is not purchased
//Or purchase is refunded or canceled
else{
savePurchaseValueToPref(false);
}
}
}

@Override
public void onBillingServiceDisconnected() {
}
});

purchase button:
public void purchase(View view) {
//check if service is already connected
if (billingClient.isReady()) {
initiatePurchase();
}
//else reconnect service
else{
billingClient = BillingClient.newBuilder(this).enablePendingPurchases().setListener(this).build();
billingClient.startConnection(new BillingClientStateListener() {
@Override
public void onBillingSetupFinished(BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
initiatePurchase();
} else {
Toast.makeText(getApplicationContext(),"Error "+billingResult.getDebugMessage(),Toast.LENGTH_SHORT).show();
}
}
@Override
public void onBillingServiceDisconnected() {
}
});
}
}
購買初始化:
private void initiatePurchase() {
List<String> skuList = new ArrayList<>();
skuList.add(PRODUCT_ID);
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
params.setSkusList(skuList).setType(INAPP);
billingClient.querySkuDetailsAsync(params.build(),
new SkuDetailsResponseListener() {
@Override
public void onSkuDetailsResponse(BillingResult billingResult,
List<SkuDetails> skuDetailsList) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
if (skuDetailsList != null && skuDetailsList.size() > 0) {
BillingFlowParams flowParams = BillingFlowParams.newBuilder()
.setSkuDetails(skuDetailsList.get(0))
.build();
billingClient.launchBillingFlow(Buy.this, flowParams);
}
else{
//try to add item/product id "purchase" inside managed product in google play console
Toast.makeText(getApplicationContext(),"Purchase Item not Found",Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(getApplicationContext(),
" Error "+billingResult.getDebugMessage(), Toast.LENGTH_SHORT).show();
}
}
});
}
購買狀態更新:
@Override
public void onPurchasesUpdated(BillingResult billingResult, @Nullable List<Purchase> purchases) {
//if item newly purchased
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK && purchases != null) {
handlePurchases(purchases);
}
//if item already purchased then check and reflect changes
else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
Purchase.PurchasesResult queryAlreadyPurchasesResult = billingClient.queryPurchases(INAPP);
List<Purchase> alreadyPurchases = queryAlreadyPurchasesResult.getPurchasesList();
if(alreadyPurchases!=null){
handlePurchases(alreadyPurchases);
}
}
//if purchase cancelled
else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
Toast.makeText(getApplicationContext(),"Purchase Canceled",Toast.LENGTH_SHORT).show();
}
// Handle any other error msgs
else {
Toast.makeText(getApplicationContext(),"Error "+billingResult.getDebugMessage(),Toast.LENGTH_SHORT).show();
}
}
void handlePurchases(List<Purchase> purchases) {
for(Purchase purchase:purchases) {
//if item is purchased
if (PRODUCT_ID.equals(purchase.getSku()) && purchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED)
{
if (!verifyValidSignature(purchase.getOriginalJson(), purchase.getSignature())) {
// Invalid purchase
// show error to user
Toast.makeText(getApplicationContext(), "Error : Invalid Purchase", Toast.LENGTH_SHORT).show();
return;
}
// else purchase is valid
//if item is purchased and not acknowledged
if (!purchase.isAcknowledged()) {
AcknowledgePurchaseParams acknowledgePurchaseParams =
AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.build();
billingClient.acknowledgePurchase(acknowledgePurchaseParams, ackPurchase);
}
//else item is purchased and also acknowledged
else {
// Grant entitlement to the user on item purchase
// restart activity
if(!getPurchaseValueFromPref()){
savePurchaseValueToPref(true);
Toast.makeText(getApplicationContext(), "Item Purchased", Toast.LENGTH_SHORT).show();
this.recreate();
}
}
}
//if purchase is pending
else if( PRODUCT_ID.equals(purchase.getSku()) && purchase.getPurchaseState() == Purchase.PurchaseState.PENDING)
{
Toast.makeText(getApplicationContext(),
"Purchase is Pending. Please complete Transaction", Toast.LENGTH_SHORT).show();
}
//if purchase is unknown
else if(PRODUCT_ID.equals(purchase.getSku()) && purchase.getPurchaseState() == Purchase.PurchaseState.UNSPECIFIED_STATE)
{
savePurchaseValueToPref(false);
purchaseStatus.setText("Purchase Status : Not Purchased");
purchaseButton.setVisibility(View.VISIBLE);
Toast.makeText(getApplicationContext(), "Purchase Status Unknown", Toast.LENGTH_SHORT).show();
}
}
}

購買回應:
AcknowledgePurchaseResponseListener ackPurchase = new AcknowledgePurchaseResponseListener() {
@Override
public void onAcknowledgePurchaseResponse(BillingResult billingResult) {
if(billingResult.getResponseCode()==BillingClient.BillingResponseCode.OK){
//if purchase is acknowledged
// Grant entitlement to the user. and restart activity

savePurchaseValueToPref(true);
Toast.makeText(getApplicationContext(), "Item Purchased", Toast.LENGTH_SHORT).show();
Buy.this.recreate();
}
}
};


購買簽章認證:
private boolean verifyValidSignature(String signedData, String signature) {
try {
// To get key go to Developer Console > Select your app > Development Tools > Services & APIs.
String base64Key =PURCHASE_KEY;
return Security.verifyPurchase(base64Key, signedData, signature);
} catch (IOException e) {
return false;
}
}

@Override
protected void onDestroy() {
super.onDestroy();
if(billingClient!=null){
billingClient.endConnection();
}
}
這樣購買的程式就結束了
下一篇附上完整版程式碼






留言