I want to establish a content provider server whose different contents have to be bought using the Android InAppBilling API. If a mobile user buys an item he gets a unique purchase token. If the user wants to download some content from my server I can use that token to check if the purchase is valid and thus the user is allowed to download the content.
But how do I check if that purchase token really belongs to the user my server is talking to? Is there an existing authentication mechanism?
as you can see at the end of image there are "response code" you can use it like developer docs
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1001) {
int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
if (resultCode == RESULT_OK) {
try {
JSONObject jo = new JSONObject(purchaseData);
String sku = jo.getString("productId");
alert("You have bought the " + sku + ". Excellent choice,
adventurer!");
}
catch (JSONException e) {
alert("Failed to parse purchase data.");
e.printStackTrace();
}
}
}
}
if not enough also you can check In-app Billing Reference (IAB Version 3)
well I am trying to help not a prof..
Related
I'm using Rave for the first time, it looked so flexible and easier to integrate, but along the line. I got stuck with testing, I'm using only card and bank payments methods.
when I choose the card to and enter some test cards from the test card link https://developer.flutterwave.com/reference#test-cards-1
I get 2 errors, => "unable to retrieve transaction fees" but in my logcat I see => " only test cards ree allowed," of which were using the test cards. And am also on staging mode which I assume is correct while testing with test keys.
When I use the bank tab I get some error in JSON toasted, but display later => "parents_limit not defined". Not just that I give this errors, I still found transactions of the bank payment types recorded on my dashboard. This confuses me more since I didn't get the payment success message so I can verify and give value to the customer.
I have integrated everything as per guideline in Rave docs, which calls my make payment. from https://github.com/Flutterwave/rave-android
variable for implementation
//online payment
String[] fullname;
String email = "";
String fName = "";
String lName = "";
String narration = "Payment for Riidit App activation";
String txRef;
String country = "NG";
String currency = "NGN";
private String mUsername, userID, mEmail;
the payment method
private void makePayment(int amount) {
txRef = email + " " + UUID.randomUUID().toString();
try {
fullname = mUsername.split(" ");
fName = fullname[0];
lName = fullname[1];
} catch (NullPointerException ex) {
ex.printStackTrace();
}
/*
Create instance of RavePayManager
*/
new RavePayManager(this).setAmount(amount)
.setCountry(country)
.setCurrency(currency)
.setEmail(email)
.setfName(fName)
.setlName(lName)
.setNarration(narration)
.setPublicKey(RiiditUtilTool.getPublicKey())
.setEncryptionKey(RiiditUtilTool.getEncryptionKey())
.setTxRef(txRef)
.acceptAccountPayments(true)
.acceptCardPayments(true)
.acceptMpesaPayments(false)
.acceptGHMobileMoneyPayments(false)
.onStagingEnv(false)
.allowSaveCardFeature(true)
.withTheme(R.style.DefaultTheme)
.initialize();
}
//called from below
private void payOnline()
{
makePayment(amount);
}
public void onActivateOnlineClick(View v) {
payOnline();
}
// expecting result of payment here either fail or success
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RaveConstants.RAVE_REQUEST_CODE && data != null) {
String message = data.getStringExtra("response");
if (resultCode == RavePayActivity.RESULT_SUCCESS) {
Toast.makeText(this, "SUCCESS " + message, Toast.LENGTH_LONG).show();
// get a PIN and SERIAL no when payment succeeds
getGetPinAndSerialForPaidUser();
} else if (resultCode == RavePayActivity.RESULT_ERROR) {
Toast.makeText(this, "ERROR " + message, Toast.LENGTH_LONG).show();
} else if (resultCode == RavePayActivity.RESULT_CANCELLED) {
Toast.makeText(this, "Payment CANCELLED " + message, Toast.LENGTH_LONG).show();
}
}
}
I want when this event is triggered, it will make the payment to using the value set in the amount and return status in the onActivityResult above where I can check and give value to the user
First of all make sure you are using the api keys which are for testing and if you are on staging mode(testing) then set this to true .onStagingEnv(true) and test after that.
I am implementing the new billing API 2.0 which has this purchase acknowledge method.
Earlier i was using AIDL for my purchase and i had following use case is that i used to pass developer payload during initiating purchase and used to get back my developer payload in the response as a part of purchase object.
Bundle bundle = mService.getBuyIntent(3, "com.example.myapp",
MY_SKU, "subs", developerPayload);
PendingIntent pendingIntent = bundle.getParcelable(RESPONSE_BUY_INTENT);
if (bundle.getInt(RESPONSE_CODE) == BILLING_RESPONSE_RESULT_OK) {
// Start purchase flow (this brings up the Google Play UI).
// Result will be delivered through onActivityResult().
startIntentSenderForResult(pendingIntent, RC_BUY, new Intent(),
Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0));
}
and in my on on activity result i used to get purchase object
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1001) {
int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
if (resultCode == RESULT_OK) {
try {
JSONObject jo = new JSONObject(purchaseData);
String sku = jo.getString("productId");
alert("You have bought the " + sku + ". Excellent choice,
adventurer!");
}
catch (JSONException e) {
alert("Failed to parse purchase data.");
e.printStackTrace();
}
}
}
}
This purchase object has a developer payload which i send to the server for verification.
Now in the new API the developer payload can be added only after purchase is completed or when we consume a purchase, so the problem is after purchase is acknowledged i need the updated purchase object but how to get it?
if (!purchase.isAcknowledged()) {
AcknowledgePurchaseParams acknowledgePurchaseParams =
AcknowledgePurchaseParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken())
.setDeveloperPayload(/* my payload */)
.build();
client.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);
}
In acknowledgePurchaseResponseListener i get only the response code whether it is success or failure. But i need to updated purchase object object with developerPayload and isAcknowledged flag true.
Is there a way to do so? Could not find anything in documentation.
The local cache of purchases is updated by the time your acknowledgePurchaseResponseListener is called so you can query the purchase from the cache using https://developer.android.com/reference/com/android/billingclient/api/BillingClient.html#querypurchases. We will consider adding the update purchase to the listener for a future library release to make this more convenient.
When performing a payment with Google Pay on my Android app, once the payment is validated, I get a PaymentData variable from which I can get some information, like email, shipping address, billing address, ...
Is there a way to get these information on my backend, without sending them in order to avoid alteration?
For instance, when I perform a Google Connect, with OAuth, I get a token which allows me to perform a request to Google's servers and retrieve by myself data like "mail", ... This way I can check if the mail was altered.
Thanks,
EDIT: here is the code from the Android application
private void payWithGooglePay() {
PaymentDataRequest.Builder request =
PaymentDataRequest.newBuilder()
.setEmailRequired(true)
.setShippingAddressRequired(needShippingAddress)
.setTransactionInfo(
TransactionInfo.newBuilder()
.setTotalPriceStatus(WalletConstants.TOTAL_PRICE_STATUS_FINAL)
.setTotalPrice(String.valueOf(amountToPay / 100))
.setCurrencyCode(currency.getCurrencyCode())
.build())
.addAllowedPaymentMethod(WalletConstants.PAYMENT_METHOD_CARD)
.addAllowedPaymentMethod(WalletConstants.PAYMENT_METHOD_TOKENIZED_CARD)
.setCardRequirements(
CardRequirements.newBuilder()
.setBillingAddressRequired(needBillingAddress)
.addAllowedCardNetworks(Arrays.asList(
WalletConstants.CARD_NETWORK_VISA,
WalletConstants.CARD_NETWORK_MASTERCARD))
.build());
PaymentMethodTokenizationParameters tokenizationParameters =
PaymentMethodTokenizationParameters.newBuilder()
.setPaymentMethodTokenizationType(WalletConstants.PAYMENT_METHOD_TOKENIZATION_TYPE_PAYMENT_GATEWAY)
.addParameter("gateway", "stripe")
.addParameter("stripe:publishableKey", liveMode ? getString(R.string.stripe_live_pk_key) : getString(R.string.stripe_test_pk_key))
.addParameter("stripe:version", "5.1.0")
.build();
PaymentDataRequest paymentDataRequest = request
.setPaymentMethodTokenizationParameters(tokenizationParameters)
.build();
if (paymentDataRequest == null) {
return;
}
AutoResolveHelper.resolveTask(paymentsClient.loadPaymentData(paymentDataRequest),
getActivity(), ProgressViewActivity.PAYMENT_WITH_GOOGLE_PAY_REQUEST_CODE);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case ProgressViewActivity.PAYMENT_WITH_GOOGLE_PAY_REQUEST_CODE:
if (resultCode == Activity.RESULT_OK) {
PaymentData paymentData = PaymentData.getFromIntent(data);
Log.d("MyGooglePay", "Google transaction ID: " + paymentData.getGoogleTransactionId());
Log.d("MyGooglePay", "mail: " + paymentData.getEmail());
Log.d("MyGooglePay", "token:" + paymentData.getPaymentMethodToken().getToken());
Log.d("MyGooglePay", "extra: " + (paymentData.getExtraData() != null ? paymentData.getExtraData().toString() : ""));
CardInfo cardInfo = paymentData.getCardInfo();
String description = cardInfo.getCardDescription();
Log.d("MyGooglePay", description);
UserAddress shippingAddress = paymentData.getShippingAddress();
UserAddress billingAddress = cardInfo.getBillingAddress();
Log.d("MyGooglePay", "shipping: " + shippingAddress);
Log.d("MyGooglePay", "billing: " + billingAddress);
String rawToken = paymentData.getPaymentMethodToken().getToken();
// Now that you have a Stripe token object, charge that by using the id
Token stripeToken = Token.fromString(rawToken);
if (stripeToken != null) {
// This chargeToken function is a call to your own server, which should then connect
// to Stripe's API to finish the charge.
Log.d("MyGooglePay", stripeToken.getCard().toJson().toString());
//sendFinalRequestAndGoToNext(stripeToken.getId());
}
break;
}
}
}
And just to be clear, what I want, is to get PaymentData directly from my backend instead of sending them from the app to my backend, to avoid alteration.
this decoupled behavior from processors and gateways is intended, and it's there to maximize compatibility for all agents involved.
Sensitive information retrieved from Google Pay is encrypted such that only the processor of the payment can reveal the contents of the payload. Tampering with the content in the middle will simply render the payload invalid.
To be able to access that content, you can also opt to implement Google Pay using the DIRECT integration. Said so, note that payment processing involves complexity and compliance with regulatory terms like PCI DSS, and because of that, not recommended unless necessary otherwise.
I have authorized Paypal amount using PayPalPayment.PAYMENT_INTENT_AUTHORIZE intent in PaymentActivity now i have authorizationId but how to capture this amount now?
Code to start PaymentActivity
PayPalPayment thingToBuy = getStuffToBuy(PayPalPayment.PAYMENT_INTENT_AUTHORIZE);
/*
* See getStuffToBuy(..) for examples of some available payment options.
*/
Intent intent = new Intent(SampleActivity.this, PaymentActivity.class);
// send the same configuration for restart resiliency
intent.putExtra(PayPalService.EXTRA_PAYPAL_CONFIGURATION, config);
intent.putExtra(PaymentActivity.EXTRA_PAYMENT, thingToBuy);
startActivityForResult(intent, REQUEST_CODE_PAYMENT);
onActivityResult
if (requestCode == REQUEST_CODE_PAYMENT) {
if (resultCode == Activity.RESULT_OK) {
PaymentConfirmation confirm =
data.getParcelableExtra(PaymentActivity.EXTRA_RESULT_CONFIRMATION);
ProofOfPayment proof = confirm.getProofOfPayment();
PayPalPayment payment = confirm.getPayment();
payment.enablePayPalShippingAddressesRetrieval(true);
JSONObject object = proof.toJSONObject();
String authID = "";
try {
authID = object.getString("authorization_id");
} catch (JSONException e) {
e.printStackTrace();
}
if (confirm != null) {
try {
Log.i(TAG, confirm.toJSONObject().toString(4));
Log.i(TAG, confirm.getPayment().toJSONObject().toString(4));
/**
* TODO: send 'confirm' (and possibly confirm.getPayment() to your server for verification
* or consent completion.
* See https://developer.paypal.com/webapps/developer/docs/integration/mobile/verify-mobile-payment/
* for more details.
*
* For sample mobile backend interactions, see
* https://github.com/paypal/rest-api-sdk-python/tree/master/samples/mobile_backend
*/
displayResultText(authID);
} catch (JSONException e) {
Log.e(TAG, "an extremely unlikely failure occurred: ", e);
}
}
} else if (resultCode == Activity.RESULT_CANCELED) {
Log.i(TAG, "The user canceled.");
} else if (resultCode == PaymentActivity.RESULT_EXTRAS_INVALID) {
Log.i(
TAG,
"An invalid Payment or PayPalConfiguration was submitted. Please see the docs.");
}
}
Now I have authorization id in authId now i want to capture this amount. Can I do this within my app without any server side work.
Make a post request to https://api.sandbox.paypal.com/v1/oauth2/token
2.Set the Authorization type to Basic Auth and then set the client_id into the Username field and secret into the Password field.
3.Set the Body to x-www-form-urlencoded and then set grant_type in the key field and client_credentials in the value field.
In response to above you will get an access token.
Now make GET request to https://api.sandbox.paypal.com/v1/payments/payment/PAY-5YK922393D847794YKER7MUI with Headers, Content-Type -- application/json" and Authorization -- Bearer accessToken_you_get_in_above_call .(PAY-5YK922393D847794YKER7MUI this is your payment id)
In response of above you will get an object as "state": "approved", if it is approved means your payment has been successfully done.
Note:- In the first response you will get an access token, but this token is valid for only few seconds , so you have to get another access token if it is expired.
For more details refer:- https://developer.paypal.com/webapps/developer/docs/integration/mobile/verify-mobile-payment/ https://developer.paypal.com/docs/integration/direct/make-your-first-call/
I am testing In-App Purchase using Static References.
I am using the Static Reference android.test.purchased but i can't figure out how to get the response of the Test Purchase in my onActivityResult(...) Method.
Here is my onActivityResult(....) Method's Body:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
// TODO Auto-generated method stub
super.onActivityResult(requestCode, resultCode, data);
Log.d("In-App", "Data = "+data.toString());
Log.d("In-App", "Request Code = "+requestCode);
Log.d("In-App", "Result Code = "+resultCode);
}
My Logcat says:
08-16 10:48:10.114: D/In-App(3889): Data = Intent { (has extras) }
08-16 10:48:10.114: D/In-App(3889): Request Code = 666
08-16 10:48:10.114: D/In-App(3889): Result Code = -1
Screen Shots of my Test Purchase:
1.
2.
The question is that, what extras should i extract from the Data Object in my onActivityResult(....) method, to detect the status of the current test purchase (Canceled, already purchased, Successfully accepted, etc) and the information about the product (JSON respnse)?
Any help is highly appreciated, thanks.
I have updated my onActivityResult(...) Method
int responseCode = data.getIntExtra("RESPONSE_CODE", 0);
String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
if (resultCode == RESULT_OK) {
//Purchased Status = Purchased
try {
//Product Details JSON
JSONObject jo = new JSONObject(purchaseData);
//Purchased Product ID
String sku = jo.getString("productId");
}
catch (JSONException e) {
e.printStackTrace();
}
}
else {
//Purchased Status = Not Purchased
}
This way i have got the purchase's status and the purchased product's details JSON.
First of all, you need a database to persist the orders. Second of all, when the user install the your app. You have to issue a RESTORE_TRANSACTIONS what the user has bought and insert the result into the local database. you can read more HERE.
EDITED:
For more details check out In-App Billing Integration