Author: Smin Rana

  • Auto-renewable subscriptions with SwiftUI

    Auto-renewable subscriptions with SwiftUI

    Auto-renewable subscriptions with SwiftUI

    Read about App Store Server Integration in Laravel

    Auto-renewable subscriptions provide access to content, services, or premium features in your app on an ongoing basis. They automatically renew at the end of their duration until the user chooses to cancel. Subscriptions are available on iOS, iPadOS, macOS, watchOS, and tvOS.

    Great subscription apps justify the recurring payment by providing ongoing value and continually innovating the app experience. If you’re considering implementing the subscription model, plan to regularly update your app with feature enhancements or expanded content.

    To offer subscriptions, youʼll need to configure them in App Store Connect and use StoreKit APIs in your app. You’ll also need to assign each subscription to a subscription group (a group of subscriptions with different access levels, prices, and durations that people can choose from), then add details such as a name, price, and description. This information displays in the In-App Purchases section of your app’s product page on the App Store. Ensure that the subscriptions are available across all device types that your app supports. Consider allowing a way for subscribers to see the status of their subscription within your app, along with upgrade, crossgrade, and downgrade options, as well as a way to easily manage or turn off their auto-renewable subscription. Make sure to follow our design and review guidelines.

    To get ready:

    Before creating any subscription make sure you have done all these steps and all are showing Active status. 

    Auto-renewable subscriptions with SwiftUI

    Go here https://appstoreconnect.apple.com/agreements/#

    Creating subscriptions

    Go to app detail page and click on the In-App purchase link under Feature. 

    Select Auto-Renewable Subscription and click the Create button.

    Auto-renewable subscriptions with SwiftUI

    SwiftUI View-Model

    //
    //  AppStoreManager.swift
    //  ARSubscription
    //
    //  Created by Smin Rana on 2/1/22.
    //
    
    import SwiftUI
    import StoreKit
    import Combine
    
    class AppStoreManager: NSObject, ObservableObject, SKProductsRequestDelegate, SKPaymentTransactionObserver {
    
        @Published var products = [SKProduct]()
        
        override init() {
            super.init()
            
            SKPaymentQueue.default().add(self)
        }
        
        func getProdcut(indetifiers: [String]) {
            print("Start requesting products ...")
            let request = SKProductsRequest(productIdentifiers: Set(indetifiers))
            request.delegate = self
            request.start()
        }
        
        func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
            print("Did receive response")
                    
            if !response.products.isEmpty {
                for fetchedProduct in response.products {
                    DispatchQueue.main.async {
                        self.products.append(fetchedProduct)
                    }
                }
            }
            
            for invalidIdentifier in response.invalidProductIdentifiers {
                print("Invalid identifiers found: \(invalidIdentifier)")
            }
        }
        
        func request(_ request: SKRequest, didFailWithError error: Error) {
            print("Request did fail: \(error)")
        }
        
        // Transaction
        
        @Published var transactionState: SKPaymentTransactionState?
        
        func purchaseProduct(product: SKProduct) {
            if SKPaymentQueue.canMakePayments() {
                let payment = SKPayment(product: product)
                SKPaymentQueue.default().add(payment)
            } else {
                print("User can't make payment.")
            }
        }
        
        struct PaymentReceiptResponseModel: Codable {
            var status: Int
            var email: String?
            var password: String?
            var message: String?
        }
        
        func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
            for transaction in transactions {
                switch transaction.transactionState {
                case .purchasing:
                    self.transactionState = .purchasing
                case .purchased:
                    print("===============Purchased================")
                    UserDefaults.standard.setValue(true, forKey: transaction.payment.productIdentifier)
                    if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL,
                        FileManager.default.fileExists(atPath: appStoreReceiptURL.path) {
    
                        do {
                            let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
                            let receiptString = receiptData.base64EncodedString(options: [])
                            
                            // TODO: Send your receiptString to the server and verify with Apple
                            // receiptString should be sent to server as JSON
                            // {
                            //    "receipt" : receiptString
                            // }
                            
                            self.transactionState = .purchased // only if server sends successful response
                        }
                        catch { print("Couldn't read receipt data with error: " + error.localizedDescription) }
                    }
                case .restored:
                    UserDefaults.standard.setValue(true, forKey: transaction.payment.productIdentifier)
    
                    queue.finishTransaction(transaction)
                    print("==================RESTORED State=============")
                    self.transactionState = .restored
                case .failed, .deferred:
                    print("Payment Queue Error: \(String(describing: transaction.error))")
                    queue.finishTransaction(transaction)
                    self.transactionState = .failed
                default:
                    print(">>>> something else")
                    queue.finishTransaction(transaction)
                }
            }
        }
        
        func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
            print("===============Restored================")
            if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL,
                FileManager.default.fileExists(atPath: appStoreReceiptURL.path) {
    
                do {
                    let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
                    let receiptString = receiptData.base64EncodedString(options: [])
                    
                    // TODO: Send your receiptString to the server and verify with Apple
                    // receiptString should be sent to server as JSON
                    // {
                    //    "receipt" : receiptString
                    // }
                    
                    
                    self.transactionState = .purchased // only if server sends successful response
                }
                catch { print("Couldn't read receipt data with error: " + error.localizedDescription) }
            }
        }
        
        func restorePurchase() {
            SKPaymentQueue.default().restoreCompletedTransactions()
        }
    }
    
    

    Testing with .storekit file on simulator:

    Server side with PHP and Laravel:

    Create app specific shared secret

    Auto-renewable subscriptions with SwiftUI

    PHP and Laravel

    <?php 
    
    // Laravel and GuzzleHTTP\Client
    
    function verifyReceiptWithApple() {
            $input = file_get_contents('php://input');
            $request = json_decode($input);
    
            $d = $request->receipt;
            $secret = 'your_app_purchase_secret';
    
            //$url = 'https://sandbox.itunes.apple.com/verifyReceipt';
            $url = 'https://buy.itunes.apple.com/verifyReceipt';
    
            // Replace with curl if you are not using Laravel
            $client = new Client([
                'headers' => [ 'Content-Type' => 'application/json' ]
            ]);
            
            $response = $client->post($url,
                ['body' => json_encode(
                    [
                        'receipt-data' => $d,
                        'password' => $secret,
                        'exclude-old-transactions' => false
                    ]
                )]
            );
    
            $json = json_decode($response->getBody()->getContents());
            if ($json->status == 0) {
    
                $email = "";
    
                // Get original transaction id
                $receipts = $json->receipt->in_app;
                if (!empty($receipts) && count($receipts) > 0) {
                    $first_receipt = $receipts[0];
                    if ($first_receipt->in_app_ownership_type == "PURCHASED") {
                        $original_transaction_id = $first_receipt->original_transaction_id;
    
                        // Create email address with transaction id 
                        // Create new user if not exists
                        $email = $original_transaction_id.'@domain.com';
                        $have_user = "check_with_your_database";
                        if (!$have_user) {
                            // New purchase -> user not found
                        } else {
                            // Restore purchase -> user found 
                            
                        }
                    }
                }
    
                return response()->json(["status" => 1, "message" => "Receipt is verified"]);
            } else {
                return response()->json(["status" => 0, "message" => "Invalid receipt"]);
            }
        }

    Things to include in-app purchase

    • Value proposition
    • Call to action
    • Clear terms
    • Signup
    • Multiple tiers
    • Log in
    • Restore
    • Terms and conditions

    Read more

    App store distribution and marketing: https://developer.apple.com/videos/app-store-distribution-marketing

    Auto-Renewable Subscriptions: https://developer.apple.com/design/human-interface-guidelines/in-app-purchase/overview/auto-renewable-subscriptions/

    Architecting for subscriptions: https://developer.apple.com/videos/play/wwdc2020/10671/

    Download full source code

    Spread the love
  • Laravel Sanctum with JavaScript

    Laravel Sanctum with JavaScript

    Laravel Sanctum with JavaScript

    Laravel Sanctum is a package for Laravel that provides a lightweight authentication system for single page applications (SPAs), mobile applications, and simple token-based APIs. It allows for the generation of API tokens that can be used to authenticate requests to your Laravel application without the need for a traditional login process. This can be useful for applications that need to make API requests to your Laravel application from a frontend JavaScript framework like React or Vue.js, or for mobile applications that need to authenticate users without a server-side session.

    How to integrate in JavaScript

    1. Install the Laravel Sanctum package by running the following command in your terminal:
    composer require laravel/sanctum

    2. Publish the package’s configuration and migration files by running the following command:

    php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"

    3. In your config/auth.php file, make sure the driver option for the api guard is set to sanctum.

    4. In your .env file, make sure the SANCTUM_STATEFUL_DOMAINS variable is set to a comma-separated list of domains that should be able to make stateful requests (requests that maintain a user session) to your application.

    5. Run the migration to create the necessary database tables by running the following command:

    php artisan migrate

    6. In your frontend JavaScript code, you can use Laravel Sanctum’s JavaScript library to make API requests to your Laravel application and authenticate them using an API token. To do this, you will need to first install the library using npm or yarn:

    npm install @laravel/sanctum

    7. In your JavaScript code, you can then use the createToken method to create a new API token for a given user, and the authenticate method to attach the token to subsequent API requests:

    import { createToken, authenticate } from '@laravel/sanctum';
    
    // Create a new API token for the authenticated user
    createToken('user-id')
        .then(response => {
            // Attach the token to subsequent API requests
            authenticate(response.token);
        });
    

    8. In your Laravel routes, you can use the auth:sanctum middleware to protect routes that require authentication:

    Route::get('/profile', function () {
        // Only authenticated users with a valid API token can access this route
    })->middleware('auth:sanctum');
    

    You can also check the Laravel Sanctum documentation for more detailed instructions and additional features.

    Read More About Laravel Sanctum

    Spread the love
  • Convert double to string in Swift

    Convert double to string in Swift

    Convert double to string in Swift

    To convert a Double to a String in Swift, you can use the String(doubleValue) initializer. For example:

    let myDouble = 3.14159
    let myString = String(myDouble)
    

    This will create a new String with the value “3.14159”.

    Alternatively, you can use string interpolation to include the value of the Double in a string. This allows you to include the double value in a string that contains other text or characters. For example:

    let myDouble = 3.14159
    let myString = "The value of pi is: \(myDouble)"

    This will create a new String with the value “The value of pi is: 3.14159”.

    Spread the love