In-App Purchase in Swift with a Single Product

in-app purchase in Swift with a single product

Step 0: In your iTunes Connect account, create an In-App purchase.

For Single purchases, use this method:

  1. Import
import StoreKit

  1. Conform to StoreKit Delegate
class YOURViewController: UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver {

  1. Use a user default to track transactions
let defaults = NSUserDefaults.standardUserDefaults()

  1. Product ID. This one will be same as in your iTunes Connect in app purchase
var product_id: NSString?

override func viewDidLoad() {
product_id = "YOUR_PRODUCT_ID"
super.viewDidLoad()
SKPaymentQueue.defaultQueue().addTransactionObserver(self)

//Check if product is purchased

if (defaults.boolForKey("purchased")){
// Hide a view or show content depends on your requirement
overlayView.hidden = true
} else if (!defaults.boolForKey("stonerPurchased")) {
print("false")
}
}

  1. Unlock Content. This is button action which will initialize purchase
@IBAction func unlockAction(sender: AnyObject) {

print("About to fetch the products")

// We check that we are allow to make the purchase.
if (SKPaymentQueue.canMakePayments()) {
var productID:NSSet = NSSet(object: self.product_id!);
var productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID);
productsRequest.delegate = self;
productsRequest.start();
println("Fetching Products");
} else {
print("can't make purchases");
}
}

  1. Helper Methods
func buyProduct(product: SKProduct) {
println("Sending the Payment Request to Apple");
var payment = SKPayment(product: product)
SKPaymentQueue.defaultQueue().addPayment(payment);
}

  1. Delegate Methods for IAP
func productsRequest (request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {

var count : Int = response.products.count
if (count>0) {
var validProducts = response.products
var validProduct: SKProduct = response.products[0] as SKProduct
if (validProduct.productIdentifier == self.product_id) {
print(validProduct.localizedTitle)
print(validProduct.localizedDescription)
print(validProduct.price)
buyProduct(validProduct);
} else {
print(validProduct.productIdentifier)
}
} else {
print("nothing")
}
}

func request(request: SKRequest!, didFailWithError error: NSError!) {
print("Error Fetching product information");
}

func paymentQueue(queue: SKPaymentQueue!, updatedTransactions transactions: [AnyObject]!) {
print("Received Payment Transaction Response from Apple");

for transaction:AnyObject in transactions {
if let trans:SKPaymentTransaction = transaction as? SKPaymentTransaction{
switch trans.transactionState {
case .Purchased:
print("Product Purchased");
SKPaymentQueue.defaultQueue().finishTransaction(transaction as SKPaymentTransaction)
defaults.setBool(true , forKey: "purchased")
overlayView.hidden = true
break;
case .Failed:
print("Purchased Failed");
SKPaymentQueue.defaultQueue().finishTransaction(transaction as SKPaymentTransaction)
break;
case .Restored:
print("Already Purchased");
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
default:
break;
}
}
}
}

Swift > 3.0

import StoreKit
class YOURVIEWController:UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver {

let product_id: NSString = "com.some.inappid" // <!-- Change it to your inapp id

In your viewDidLoad add

override func viewDidLoad() {
super.viewDidLoad()
SKPaymentQueue.default().add(self)

In your buy button action

@IBAction func buyNowAction(_ sender: UIButton) {

if (SKPaymentQueue.canMakePayments()) {
let productID:NSSet = NSSet(array: [self.product_id as NSString]);
let productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>);
productsRequest.delegate = self;
productsRequest.start();
print("Fetching Products");
} else {
print("can't make purchases");
}
}

In Your Restore button action

// MARK: - Restore In App Purchase
@IBAction func restoreAction(_ sender: UIButton) {

if (SKPaymentQueue.canMakePayments()) {
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().restoreCompletedTransactions()
} else {
// show error
}

}

Add Delegates:

// SKProductRequest Delegate

func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {

print(response.products)
let count : Int = response.products.count
if (count>0) {

let validProduct: SKProduct = response.products[0] as SKProduct
if (validProduct.productIdentifier == self.product_id as String) {
print(validProduct.localizedTitle)
print(validProduct.localizedDescription)
print(validProduct.price)
self.buyProduct(product: validProduct)
} else {
print(validProduct.productIdentifier)
}
} else {
print("nothing")
}
}

func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for transaction:AnyObject in transactions {
if let trans:SKPaymentTransaction = transaction as? SKPaymentTransaction{

self.dismissPurchaseBtn.isEnabled = true
self.restorePurchaseBtn.isEnabled = true
self.buyNowBtn.isEnabled = true

switch trans.transactionState {
case .purchased:
print("Product Purchased")
//Do unlocking etc stuff here in case of new purchase
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)

break;
case .failed:
print("Purchased Failed");
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
break;
case .restored:
print("Already Purchased")
//Do unlocking etc stuff here in case of restor

SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
default:
break;
}
}
}
}

//If an error occurs, the code will go to this function
func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
// Show some alert
}

iOS In App Purchase buy several products

You cannot purchase multiple products in a single transaction unless they are multiple of the same product (you can set a quantity of up to 10).

Perhaps consider adding IAPs that combine the benefits of groups of your existing IAPs (for example if you sell "level 1" "level 2" "level 3" consider selling "level 1-3" as well.

Unrelated, but you might want to observe the payment queue only once, rather than trying to add yourself as an observer each time a purchase is attempted.

Payment is described here and only discussed buying one product at a time:

https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/RequestPayment.html#//apple_ref/doc/uid/TP40008267-CH4-SW2

In-App Purchase & Restore Button : Single Product - Non-Consumable

I have amended my code for the InAppPViewController.swift file as follows:

//  InAppPViewController.swift

import UIKit
import StoreKit

class InAppPViewController: UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver {

let defaults = NSUserDefaults.standardUserDefaults()
var product_id: NSString?;

override func viewDidLoad() {
super.viewDidLoad()

// Do any additional setup after loading the view.
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

@IBAction func restorePurchases(sender: UIButton) {
// Set up the observer
SKPaymentQueue.defaultQueue().addTransactionObserver(self)

//Check if user can make payments and then proceed to restore purchase
if (SKPaymentQueue.canMakePayments()) {
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
}

}

@IBAction func unlockAction(sender: AnyObject) {

product_id = "some.product.id";
// Adding the observer
SKPaymentQueue.defaultQueue().addTransactionObserver(self)

//Check if product is purchased
if (defaults.boolForKey("purchased")){
print("User already purchased this")
// Hide a view or show content depends on your requirement
}

else if (!defaults.boolForKey("Purchased")){
print("User has not yet pur hased this")
}

print("About to fetch the products");

// Check if user can make payments and then proceed to make the purchase.
if (SKPaymentQueue.canMakePayments())
{
let productID:NSSet = NSSet(object: self.product_id!);
let productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>);
productsRequest.delegate = self;
productsRequest.start();
print("User can make purchases and will fetch products from Apple Store now");
}else{
print("User can't make purchases");
}

}

func buyProduct(product: SKProduct){
print("Sending the Payment Request to Apple");
let payment = SKPayment(product: product)
SKPaymentQueue.defaultQueue().addPayment(payment);

}

func productsRequest (request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {

let count : Int = response.products.count
if (count>0) {

let validProduct: SKProduct = response.products[0] as SKProduct
if (validProduct.productIdentifier == self.product_id) {
print(validProduct.localizedTitle)
print(validProduct.localizedDescription)
print(validProduct.price)
buyProduct(validProduct);
} else {
print(validProduct.productIdentifier)
}
} else {
print("nothing")
}
}

func request(request: SKRequest, didFailWithError error: NSError) {
print("Error Fetching product information");
}

// Allowing for all possible outcomes:
func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
print("Received Payment Transaction Response from Apple");

for transaction:AnyObject in transactions {
if let trans:SKPaymentTransaction = transaction as? SKPaymentTransaction {
switch trans.transactionState {

case .Purchased:
print("Product Purchased")
let alert = UIAlertView(title: "Thank You", message: "Thank you for your purchase!", delegate: nil, cancelButtonTitle: "OK")
alert.show();
SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
defaults.setBool(true , forKey: "purchased")
break;

case .Failed:
print("Purchased Failed");
SKPaymentQueue.defaultQueue().finishTransaction(transaction as! SKPaymentTransaction)
break;

case .Restored:
print("Already Purchased");
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
break;

default:
break;
}
}
}

}

}

I left the ViewController.swift file as is.

Product purchases seem to work now.

But regarding Restore Purchases, I can run the code on my physical device, but cannot test the Restore Purchases function.

I am caught with previous Restore Purchases that are still unresolved and looping in the system. I am unable to clear my SKPaymentsQueue manually. Thus my code refuses to entertain anymore new Restore Purchase requests.

How to set up a single In-App Purchase Product having multiple Price Tiers?

You will have to create different identifiers for every In-App Purchase unfortunately.

From the accepted answer here In-app purchase custom price:

As noted by other poster, you cannot have variable in-app purchases.
You have to select one of the provided price tiers.

While you aren't exactly making it so the price is changing a lot (since your idea isn't a delivery service), if it is going to change at all you are going to need to create multiple identifiers.

Swift IOS 9 - Implement an in app purchase in the simplest way possible

The following works in Swift 2.

First, In Itunes Connect make an IAP

Import Storekit

import StoreKit

Add the StoreKit Delegates

class ViewController: UIViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver {

Declare your product id

var product_id: NSString?

override func viewDidLoad() {

product_id = "YOUR_PRODUCT_ID"
super.viewDidLoad()
SKPaymentQueue.defaultQueue().addTransactionObserver(self)

//Check if product is purchased
if (NSUserDefaults.standardUserDefaults().boolForKey("purchased")){

// Hide ads
adView.hidden = true

} else {
print("Should show ads...")

}

}

Unlock Content: This is button action which will initialize purchase

@IBAction func unlockAction(sender: AnyObject) {

print("About to fetch the product...")

// Can make payments
if (SKPaymentQueue.canMakePayments())
{
let productID:NSSet = NSSet(object: self.product_id!);
let productsRequest:SKProductsRequest = SKProductsRequest(productIdentifiers: productID);
productsRequest.delegate = self;
productsRequest.start();
println("Fetching Products");
}else{
print("Can't make purchases");
}

}

Helper Methods

func buyProduct(product: SKProduct){
println("Sending the Payment Request to Apple");
let payment = SKPayment(product: product)
SKPaymentQueue.defaultQueue().addPayment(payment);

}

Delegate Methods for IAP

func productsRequest (request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {

let count : Int = response.products.count
if (count>0) {
var validProduct: SKProduct = response.products[0] as SKProduct
if (validProduct.productIdentifier == self.product_id) {
print(validProduct.localizedTitle)
print(validProduct.localizedDescription)
print(validProduct.price)
buyProduct(validProduct);
} else {
print(validProduct.productIdentifier)
}
} else {
print("nothing")
}
}

func request(request: SKRequest!, didFailWithError error: NSError!) {
print("Error Fetching product information");
}

func paymentQueue(_ queue: SKPaymentQueue,
updatedTransactions transactions: [SKPaymentTransaction])

{
print("Received Payment Transaction Response from Apple");

for transaction:AnyObject in transactions {
if let trans:SKPaymentTransaction = transaction as? SKPaymentTransaction{
switch trans.transactionState {
case .Purchased:
print("Product Purchased");
SKPaymentQueue.defaultQueue().finishTransaction(transaction as SKPaymentTransaction)
// Handle the purchase
NSUserDefaults.standardUserDefaults().setBool(true , forKey: "purchased")
adView.hidden = true
break;
case .Failed:
print("Purchased Failed");
SKPaymentQueue.defaultQueue().finishTransaction(transaction as SKPaymentTransaction)
break;

case .Restored:
print("Already Purchased");
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()

// Handle the purchase
NSUserDefaults.standardUserDefaults().setBool(true , forKey: "purchased")
adView.hidden = true
break;
default:
break;
}
}
}

}

Restore Purchases

Add this to a function:

if (SKPaymentQueue.canMakePayments()) {
SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
}


Related Topics



Leave a reply



Submit