Flutter ios appstore validateReceipt on non-consumable in-app purchase - ios

I seem to be stuck on this. Trying to validate the receipt (server side) on an in-app purchase on IOS (haven't tried with android, yet.)
I'm using the official in_app_purchase pub package.
This is the setup to initialize the purchase:
Future<bool> initiatePurchase() async {
...
(verify store is available)
..
print ("==> Store available, initiating purchase");
final PurchaseParam purchaseParam =
PurchaseParam(productDetails: _productDetails![0]);
await InAppPurchase.instance.buyNonConsumable(purchaseParam: purchaseParam);
return true;
}
Here's my verify purchase call:
Future<bool> _verifyPurchase(PurchaseDetails purchaseDetails) async {
PurchaseVerifRest purchaseRest = PurchaseVerifRest();
Map<String,dynamic> rsp = await purchaseRest.verifyPurchase(
{
"source": purchaseDetails.verificationData.source,
"vfdata": purchaseDetails.verificationData.serverVerificationData
});
// bundle up the source and verificationData in a map and send to rest
// call
return rsp['status'] == 200;
}
On the server side, the code looks like this (NodeJS/express app)
// (in router.post() call - 'purchaseData' is the map sent in the above code,
// the 'vfdata' member is the 'serverVerificationData'
//. in the 'purchaseDetails' object)
if (purchaseData.source == ('app_store')) {
const IOS_SHARED_SECRET = process.env...;
let postData = {
'receipt-data': purchaseData['vfdata'],
'password': IOS_SHARED_SECRET
};
try {
let verif_rsp = await execPost(postData);
retStatus = verif_rsp.statusCode;
msg = verif_rsp.data;
} catch (e) {
retStatus = e.statusCode;
}
}
What I get back, invariably is
210003 - Receipt could not be authenticated
... even though the purchase seems to go through, whether I validate or not.
Details/questions:
Testing with a sandbox account.
This is for a 'non-consumable' product purchase.
I'm assuming that purchaseDetails.verificationData.serverVerificationData is the payload containing the receipt to send to Apple for verification. Is this not correct? Is there another step I need to do to get the receipt data?
I've read in other posts that the verification step is only for recurring subscriptions and not for other types of products. Is this correct? I don't see anything in Apple's docs to indicate this.
Any thoughts appreciated.

Related

How to implement the in-app purchase feature in xamarin forms ios project

I am trying to implement the in-app purchase inside my Xamarin forms ios application. I have created one in-app purchase product on the app store. I need to do the payment when subscribing the application. I choose Auto-Renewable Subscription as the in-app purchase type.
After that how can I implement that feature on the app side? Do I need to use any Dependency Service for this? Which NuGet package do we need to use, is it Plugin.InAppBilling? I researched this and was confused about the app side integration. Any other setup I need to do to implement this feature.
I am looking for the specific codes that connect the application and the in-app purchase property created on the AppStore.
Only for ios, I am planning to implement the in-app purchase and for android, I am planning to do the payment with stripe. Is google accept stripe payment for this or are they also forced to implement in-app purchases?
Following is my scenario:
The subscription is for the usage of applications from the app/play store. The admin will subscribe to it and all the other users can use it for free.
I tried the below codes from this blog. But it looks likes for the android part, and not mentioned the ios part implementation.
// Connect to the service here
await CrossInAppBilling.Current.ConnectAsync();
// Check if there are pending orders, if so then subscribe
var purchases = await CrossInAppBilling.Current.GetPurchasesAsync(ItemType.InAppPurchase);
if (purchases?.Any(p => p.State == PurchaseState.PaymentPending) ?? false)
{
Plugin.InAppBilling.InAppBillingImplementation.OnAndroidPurchasesUpdated = (billingResult, purchases) =>
{
// decide what you are going to do here with purchases
// probably acknowledge
// probably disconnect
};
}
else
{
await CrossInAppBilling.Current.DisconnectAsync();
}
As per the same blog, I have updated AppDelegate like below:
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App());
//initialize current one.
Plugin.InAppBilling.InAppBillingImplementation.OnShouldAddStorePayment = OnShouldAddStorePayment;
var current = Plugin.InAppBilling.CrossInAppBilling.Current;
return base.FinishedLaunching(app, options);
}
bool OnShouldAddStorePayment(SKPaymentQueue queue, SKPayment payment, SKProduct product)
{
// true in app purchase is initiated, false cancels it.
// you can check if it was already purchased.
return true;
}
On the MainPage, they have added the below code to purchase:
private async void ButtonNonConsumable_Clicked(object sender, EventArgs e)
{
var id = "iaptest";
try
{
await CrossInAppBilling.Current.ConnectAsync();
var purchase = await CrossInAppBilling.Current.PurchaseAsync(id, ItemType.InAppPurchase);
if (purchase == null)
{
await DisplayAlert(string.Empty, "Did not purchase", "OK");
}
else
{
if (!purchase.IsAcknowledged && Device.RuntimePlatform == Device.Android)
await CrossInAppBilling.Current.AcknowledgePurchaseAsync(purchase.PurchaseToken);
await DisplayAlert(string.Empty, "We did it!", "OK");
}
}
catch (Exception ex)
{
await DisplayAlert(string.Empty, "Did not purchase: " + ex.Message, "OK");
Console.WriteLine(ex);
}
finally
{
await CrossInAppBilling.Current.DisconnectAsync();
}
}
On this code they are checking the platform is android: Device.RuntimePlatform == Device.Android. This code is on the portable project, so how I can do the same for ios? And my purchase type is auto renewable subscription, that part is empty on this blog.
private async void ButtonRenewingSub_Clicked(object sender, EventArgs e)
{
}

Flutter (IOS) in app purchase receipt data

I use the [in app purchase][1] library to make in-app purchases in the application I developed with Flutter. When a purchase is made, in order to verify in-app purchases for Android on the server side, I am sending the datas as follows, which I need to send by the server.
_verifyPurchase(PurchaseDetails purchase) async {
productID = purchase.productID;
//for android it works nice
if(Platform.isAndroid){
orderId = purchase.billingClientPurchase.orderId;
purchaseToken = purchase.billingClientPurchase.purchaseToken;
purchaseVerify(orderId, purchaseToken, productID);
//but it does not work for iOS and the data required for purchase verification does not go to the server
}else if(Platform.isIOS){
transactionId = purchase.skPaymentTransaction.originalTransaction;
verifData = purchase.verificationData.serverVerificationData;
purchaseVerify(transactionId, verifData, productID);
}
}
purchaseVerify(String orderId, String purchaseToken, String productID) async {
var data = {
'orderId' : orderId,
'purchaseToken' : purchaseToken,
'productId' : productID,
};
res = await Network().authData(data, 'purchaseVerify.php');
}
However, although I try to obtain the data required to verify the in-app purchases for iOS as follows, no data is sent to the server side.
How can I get the data needed to validate in-app purchases for iOS and send it to the server side?

Cannot receive FCM (Push Notification iOS)

I make app by React Native.
Few days ago,I was able to receive FCM. But now I cannot.
Client app permissions notification, I set Xcode(capability),firebase and Apple Developer(APNs setting).
And console.log tells me success.
But I cannot receive notification. Although few days ago I could.
Is this error in firebase??
I don't know cause, please help me m(_ _)m
=======environment==========
Xcode Version 11.3
"react-native-firebase": "^5.6.0"
"react-native": "0.61.5"
"firebase-admin": "^8.9.0"
=======addition========
Regenerating APNs and setting, I could receive notification.
But next day, I couldn't receive notification.
APNs is valid in only one day???
Most probably, in your Firebase console number of potential users are eligible for this campaign is 0 currently.
"Estimate based on approximately 0 users who are registered to receive notifications. Some targeted users will not see the message due to device inactivity. For recurring campaigns, estimate is for initial send only."
Possible solution:
1) If that's the case (0 users are registered)
--> Check if the fcmToken is received.
getFcmToken = async () => {
const fcmToken = await firebase.messaging().getToken();
if (fcmToken) {
console.log(fcmToken);
this.showAlert(‘Your Firebase Token is:’, fcmToken);
} else {
this.showAlert(‘Failed’, ‘No token received’);
}
}
2) If the number is not zero
--> Most probably your app is opened in foreground and the notification is not displayed.
--> Try to lock your iPhone screen and the notification will appear, else try to handle it which the app is in foreground
messageListener = async () => {
this.notificationListener = firebase.notifications().onNotification((notification) => {
const { title, body } = notification;
this.showAlert(title, body);
});
this.notificationOpenedListener = firebase.notifications().onNotificationOpened((notificationOpen) => {
const { title, body } = notificationOpen.notification;
this.showAlert(title, body);
});
const notificationOpen = await firebase.notifications().getInitialNotification();
if (notificationOpen) {
const { title, body } = notificationOpen.notification;
this.showAlert(title, body);
}
this.messageListener = firebase.messaging().onMessage((message) => {
console.log(JSON.stringify(message));
});
}
3) Check if the cert has expired (unlikely) since you mentioned that it's still working a few days back.

URL for cancelling an in App purchase in Apple IOS

I am using Apple with Java to validate an IN app Purchase this way
public static void validateProductPurhcaseReceipt(String receiptData, String VERIFICATION_URL)
{
Map outPut = new HashMap();
HttpClient httpClient = new DefaultHttpClient();
try {
HttpPost request = new HttpPost(VERIFICATION_URL);
JSONObject requestData = new JSONObject();
requestData.put("receipt-data", receiptData);
requestData.put("password", "f1ebdc2f49664d7188b4d83f90131ecf");
StringEntity requestEntity = new StringEntity(requestData.toString());
request.addHeader("content-type", "application/x-www-form-urlencoded");
request.setEntity(requestEntity);
HttpResponse response = httpClient.execute(request);
String responseBody = EntityUtils.toString(response.getEntity());
JSONObject responseJSON = new JSONObject(responseBody);
System.out.println(responseJSON);
}
catch (Exception ex) {
ex.printStackTrace();
}
finally {
httpClient.getConnectionManager().shutdown();
}
}
The URL i am using is for verifyReceipt is
Development mode = https://sandbox.itunes.apple.com/verifyReceipt
Production mode = https://buy.itunes.apple.com/verifyReceipt
Could you please tell me what is the URL for Cancel an in app purchase
There aren't any to cancel an IAP. If it is an auto-renewing subscription, the user has an option to cancel the subscription before next renewal from their iTunes settings. If it is a one-time purchase, the IAP either goes through or fails.
Here's an Apple support doc explaining the different types of IAPs
And if you want to find out how to cancel an IAP if verification fails, this SO question discusses it (and this).

Send notification from web to android device using Firebase

I am trying for a while now to implement this flow: When user adds some files on server app, notification should trigger and send from server to FCM and that from there to pass message saying something like: 'New file has been added'.
Basically I want to inform mobile device user that something on server has been changed.
I have tried many things, but nothing seems to work as I would expect, at least.
On the mobile side I have set up Firebase inside my Xamarin.Android project, and when I am sending notifications directly from Firebase console, I get notifications, and everything is good.
But I don't want to send notifications via Firebase console, I would rather send notification from server (which is ASP.NET MVC project) to Firebase console and then pass it from there to android device.
My first question would be: Has anybody got an idea how can I inform web app about device_id? Is there some way that android device send this information on server? And maybe from there I can store that data and update it occasionally, since it is basically a refresh token.
My second problem is this: Even when I hard code current device_id of an active android device and try to send a message from server whit this code:
public class FirebaseService : IFirebaseService
{
public void SendMessageToClientApplication(string message, string serverApiKey, string senderId, string deviceId)
{
AndroidFCMPushNotificationStatus result = new AndroidFCMPushNotificationStatus();
try
{
result.Successful = false;
result.Error = null;
deviceId = "eMk6mD8P8Dc:APA91bG5Lmqn4Hwb4RZJ1Mkdl8Rf_uYQsQCEfDJK334tzSvIGzdao7o2X6VmtcTEp_Li0mG8iUoUT7-_RnZxQKocHosZwx6ITWdpmQyCwUv60IIIy0vxNlEaccT6RqK6c-cE1C6I3FTT";
var value = message;
WebRequest tRequest = WebRequest.Create("https://fcm.googleapis.com/fcm/send");
tRequest.Method = "post";
tRequest.ContentType = "application/x-www-form-urlencoded;charset=UTF-8";
tRequest.Headers.Add(string.Format("Authorization: key={0}", serverApiKey));
tRequest.Headers.Add(string.Format("Sender: id={0}", senderId));
string postData = "collapse_key=score_update&time_to_live=108&delay_while_idle=1&data.message="
+ value + "&data.time=" + DateTime.Now.ToString() + "&registration_id=" + deviceId + "";
Byte[] byteArray = Encoding.UTF8.GetBytes(postData);
tRequest.ContentLength = byteArray.Length;
using (Stream dataStream = tRequest.GetRequestStream())
{
dataStream.Write(byteArray, 0, byteArray.Length);
using (WebResponse tResponse = tRequest.GetResponse())
{
using (Stream dataStreamResponse = tResponse.GetResponseStream())
{
using (StreamReader tReader = new StreamReader(dataStreamResponse))
{
String sResponseFromServer = tReader.ReadToEnd();
result.Response = sResponseFromServer;
}
}
}
}
}
catch (Exception ex)
{
result.Successful = false;
result.Response = null;
result.Error = ex;
}
}
}
I get nothing both in Firebase console and of course nothing on device as well.
I have tried to implement Firebase web as javascript on my server app like this:
<script>
var config = {
apiKey: "mykey",
authDomain: "myauthdomain",
databaseURL: "mydatabaseurl",
projectId: "myprojectid",
storageBucket: "mystoragebucket",
messagingSenderId: "mysenderid"
};
window.onload = function () {
firebase.initializeApp(config);
const messaging = firebase.messaging();
messaging.requestPermission()
.then(function () {
console.log('Notification permission granted.');
return messaging.getToken()
})
.then(function (token) {
console.log(token);
})
.catch(function (err) {
console.log('Unable to get permission to notify.', err);
});
messaging.onMessage(function (payload) {
console.log('onMessage: ', payload);
});
}
</script>
But this code gets some kind of a different device_id(aka token), probably one generated for that server machine.
Does anybody has experience with sending device_id to server app and from there sending notification message to Firebase console? I would appreciate some code examples, tutorials or anything that can help, since I was unable to find something useful during my google search.
My first question would be: Has anybody got an idea how can I inform web app about device_id?
The most common approach is to store the list of device tokens (each device that uses FCM has such a token) in a database, such as the Firebase Database. There is an example of this in the Cloud Functions for Firebase documentation. In this example the devices receiving the messages are web pages, but the approach is the same for iOS and Android.
I also recommend reading Sending notifications between Android devices with Firebase Database and Cloud Messaging. In this article, instead of sending to a device token, each user subscribes to a topic. That prevents having to manage the device tokens in your code.

Resources