Check if user has valid auto-renewable subscription with Parse iOS SDK - ios

I'm trying to implement an application with auto-renewable subscription. Users should pay to access all functions of my application. I already use Parse as backend for my application. It provides some API methods for inAppPurchases but there is nothing said about auto-renewable type. The only thing I have found is some two years old threads in the blog it is said that receipt verification was implemented only for downloadable purchases.
I have tried to use as it called in docs "Simple purchase" and it works fine but I can't figure out how can I check if my user already bought subscription or not.
Does anybody know is there way to do it via Parse API or This should implemented in another way?

As mentioned, receipt validation is only built into the Parse SDK for downloadable content, but it is fairly simple to to create a Cloud Code function that POSTs the app receipt to the iTunes Store for validation. Here are the Apple docs for server side validation: Validating Receipts with the App Store
Here is a what a basic function would look like:
Parse.Cloud.define('validateReceipt', function (request, response) {
var receiptAsBase64EncodedString = request.params.receiptData;
var postData = {
method: 'POST',
url: 'http://buy.itunes.apple.com/verifyReceipt',
body: { 'receipt-data': receiptAsBase64EncodedString,
'password': SHARED_SECRET }
}
Parse.Cloud.httpRequest(postData).then(function (httpResponse) {
// httpResponse is a Parse.Cloud.HTTPResponse
var json = httpResponse.data; // Response body as a JavaScript object.
var validationStatus = json.status; // App Store validation status code
var receiptJSON = json.receipt; // Receipt data as a JSON object
// TODO: You'll need to check the IAP receipts to retrieve the
// expiration date of the auto-renewing subscription, and
// determine if it is expired yet.
var subscriptionIsActive = true;
if (subscriptionIsActive) {
return response.success('Subscription Active');
}
else {
return response.error('Subscription Expired');
}
});
});
See Receipt Fields for details on interpreting the receipt JSON. It's fairly straight forward for iOS 7+, but auto-renewing subscription receipts for iOS 6 and earlier are tedious.

Related

Stripe SDK integration using swift and flutter

I am trying to integrate Apple Pay in iOS using flutter. I am using method channels to communicate with swift and get the payment process completed. I have followed the documentation which is in this link
However, I believe I have stuck in the very ending part which I don't understand how to continue the flow. Since I am using flutter UIs, I don't need iOS ViewControllers.
This is the code that I have tried so far in the AppDelegate.swift:
func handleApplePayButtonTapped(result: FlutterResult){
let merchantIdentifier = "my.apple.merchant.id"
let paymentRequest = Stripe.paymentRequest(withMerchantIdentifier:merchantIdentifier, country:"US", currency:"USD")
paymentRequest.paymentSummaryItems = [
PKPaymentSummaryItem(label:"Fancy Hat", amount:50.00),
PKPaymentSummaryItem(label:"iHats, Inc", amount:50.00),
]
if Stripe.canSubmitPaymentRequest(paymentRequest){
//next steps ???
result(String("Can submit payment request"))
}else{
result(String("Can't submit payment request"))
}
}
I am calling this code in flutter UI using this code:
Future<void> _doPayment() async {
String returnMsg;
try {
final bool result = await platform.invokeMethod('checkIfDeviceSupportsApplePay');
if(result){
final String status = await platform.invokeMethod('handleApplePayButtonTapped');
print(status);
}
returnMsg = '$result';
} on PlatformException catch (e) {
returnMsg = "Failed: '${e.message}'.";
}
print(returnMsg);}
I already have a Stripe publishable key as well as a Heroku deployed backend. If you checked my swift code, you will see where I am stuck at the moment.
As I have understood the flow, what is remaining to be done is
send the card details to the backend and get a token
using the token, send the payment details to the Stripe server
I am very new to swift language and code samples will be greatly helpful for me to continue with.
Thank you.
It looks like you're following the Stripe Custom iOS Integration, using the native PKPaymentAuthorizationViewController.
You should read through the integration steps here: https://stripe.com/docs/mobile/ios/custom#apple-pay
Basically, your next steps would be
instantiate a PKPaymentAuthorizationViewController with the paymentRequest
Set yourself to its delegate
present the PKPaymentAuthorizationViewController
implement the relevant delegate methods to get back an Apple Pay token (PKToken)
convert PKToken to STPToken (a Stripe token)
All these steps and more are detailed in the link above.

How to get the original_application_version (the first purchased version number) out of iOS Receipt in iOS 11?

I have a paid iOS App.
I need to get the original_application_version number (the first version purchased by the user) from the Apple AppStore Receipt.
To get the receipt, when my app loads, I use checkReceiptFromAppStore() function:
func checkReceiptFromAppStore() {
let receipt = self.getReceipt()
print("receipt Data is: \(receipt)") // prints this: receipt Data is: Optional(5141 bytes)
}
getReceipt() function is the following:
func getReceipt() -> Data? {
if Bundle.main.appStoreReceiptURL != nil {
print("app receipt: \(Bundle.main.appStoreReceiptURL)")
do {
let receiptData = try Data(contentsOf: Bundle.main.appStoreReceiptURL!)
return receiptData
} catch {
print("error converting receipt to Data: \(error.localizedDescription)")
}
}
return nil
}
I've watched WWDC 2017 Advanced StoreKit video about In App purchases and receipt validation and WWDC 2013 Video about using Receipts, read different
resources related to my problem (this, this, this, this, this, this, and this...), but I still don't understand what to do next to get the
"original_application_version" from the App Store Receipt. I need only this field and don't understand why is it so hard to get it.
I've read this too: https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html
I think that the receipt is not nil as long at when I run print("receipt Data is: (receipt)") it prints this: receipt Data is: Optional(5141 bytes)
I can suppose that I should parse the receipt to get that field. Can I do it using Decodable? Is there the easiest way to get this original_application_version field? Is it possible to do this without a receipt validation?
I need to get the original_application_version field only to detect the number of the first version bought by the user. If you know any other solutions to get the first purchased version number, I'd be glad to hear them.
I'm developing in Xcode 9, Swift 4, iOS 11
Any answers appreciated, thanks.
All receipt fields are in binary format within application receipt. You should use any kind of app receipt decoder in order to get original_application_version. It is always good thing to validate app receipt before using its contents. For example, you can use RMStore framework (RMStore). It contains receipt validator as well as decoder. Example Obj-C source:
RMAppReceipt *appReceipt = RMAppReceipt.bundleReceipt;
if (appReceipt != nil &&
[appReceipt.originalAppVersion length] > 0 &&
![appReceipt.originalAppVersion isEqualToString:#"1.0"]) {
//Process your original app version
} else {
//Probably it is sandbox mode or app install via Xcode
//Or maybe you should force app receipt refresh
}

Why does the latest_receipt object changing every time I request it from Apple?

There is one thing I don't quite understand when it comes to In-App Subscription purchase.
I obtain the receipt on iOS client like this:
private func loadReceipt() -> Data? {
guard let url = Bundle.main.appStoreReceiptURL else {
return nil
}
do {
let receipt = try Data(contentsOf: url)
return receipt
} catch {
print("Error loading receipt data: \(error.localizedDescription)")
return nil
}
}
And send it for verification to my server (written n Python).
def verify_receipt(self, receipt):
r = requests.post(config.APPLE_STORE_URL, json=receipt)
request_date_ms = DateUtils.generate_ms_from_current_time()
for item in r.json()['latest_receipt_info']:
expires_date_ms = int(item['expires_date_ms'])
if expires_date_ms > request_date_ms:
return True
return False
I'm not sure if this is the correct way of verifying if a subscription is still valid.
I get the expires_date_ms from latest_receipt_info, and if it's greater than the current time in milliseconds, then the subscription counts as still valid.
However what I noticed is that the separate latest_receipt, which is supposed to be equal to the one I have just sent in, is actually changing every time I call the API. But why? I haven't subscribed to anything new, why is the latest receipt changing?
According to the docs:
latest_receipt
Only returned for receipts containing auto-renewable subscriptions.
For iOS 6 style transaction receipts, this is the base-64 encoded
receipt for the most recent renewal. For iOS 7 style app receipts,
this is the latest base-64 encoded app receipt.
If this is against the sandbox then the subscription is auto-renewing on a predefined period. See:
https://help.apple.com/app-store-connect/#/dev7e89e149d

iTunes app rejected: missing feature to deliver subscription content

I'm implementing the In-App Purchase feature in my iOS app for the first time. I passed all test phases correctly and I submitted the app to review, but it was rejected for this reason:
Your app offers a content subscription but does not have a mechanism in place to support the requirement that the subscription content be available to the user on all of their iOS devices.
To fix the issue I have to:
include an optional user registration feature to deliver subscription content to all of the user's iOS devices.
My app don't require a user registration, so I'm wondering if it's really necessary.
Assuming the answer is "YES!", I don't know what really I have to do.
After the user registration, I need to save the receipt data on my host and then retrieve the data if the user change device?
Actually I get the receipt data in this way:
func validateReceipt() {
let receiptUrl = NSBundle.mainBundle().appStoreReceiptURL
let isSandox = receiptUrl?.absoluteString.containsString("sandboxReceipt")
if let receipt: NSData = NSData(contentsOfURL: receiptUrl!) {
let receiptdata: NSString = receipt.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
let transaction = GTLAppleTransactionBeanApiAppleTransactionBean()
transaction.receiptData = receiptdata as String
transaction.sandbox = isSandox
ReceiptService().validate(transaction, completion: validated)
} else {
receiptDetected = false
delegate.subscriptionUpdate()
}
}
The "GTLAppleTransactionBeanApiAppleTransactionBean()" is my GAE Bean and "ReceiptService()" call my GAE Endpoint service. This service check the receipt data with Apple.
A Swift example will be very much appreciated. :P
Sorry for my English.
Thanks, Alessio.

How to use Braintree V Zero on IOS app?

I am coding an IOS app with Payment feature.
I decided to use Braintree V Zero.
At the very beginning, I use their excellent DropIn UI feature, and everything works fine.
But when payment happen, the Drop In UI required end-user to input his credit card or Paypal information every time.
Does any expect know how to implement one automatic charge solution by BrainTree V zero?
Like Uber's charge solution.
I guess maybe need to mark the user's credit card information from app side or service side?
router.get('/token', function (req, res) {
console.log('Kevin in token be called %s', req.param('aCustomerId'));
var aCustomerId = req.param('aCustomerId');
console.log('Kevin %s', aCustomerId);
gateway.clientToken.generate({customerId: aCustomerId}, function (error, response) {
res.send(response.clientToken);
console.log(response.clientToken);
});
});
Thank you in advanced!
Full disclosure: I work for Braintree.
The Braintree drop-in will display previously used payment methods for a customer, if you pass the customer_id in when generating a client token on your server.
​
Here's an example of how to do it in Node:
gateway.clientToken.generate({
customerId: aCustomerId
}, function (err, response) {
var clientToken = response.clientToken
});
​
Once a payment method is used, it will be saved in the drop-in and the customer will not have to enter it again. Pass the token of the saved payment method when creating a transaction:
​
gateway.transaction.sale({
amount: "10.00",
paymentMethodToken: theToken,
options: {
submitForSettlement: true
}
}, function (err, result) {
});
If you have any further questions, please feel free to contact Braintree support.
3133e

Resources