# Purchases and User Info

***

Ready to work

a. Configure product information in apple store

b. Add product information in appflow\.ai platform

### **Product id**

Get product id of iOS in-app purchase

```swift
Appflow.shared.getPurchaseProductIds { productIdArray, error in 
 
 }
```

**API Reference**

| getPurchaseProductIds |                  |
| --------------------- | ---------------- |
| error = nil           | Success callback |
| error != nil          | Failure callback |

| error: Error |               |
| ------------ | ------------- |
| code         | error code    |
| message      | error message |

| Parameter returned: data type | Instructions                                             |
| ----------------------------- | -------------------------------------------------------- |
| productIdArray: \[string]     | Returns the Appflow\.ai platform configuration ProductID |

### **Product SKU**

Get the sku information of in-app purchases

Displaying products to fetch the Products sku information, you have to call method:

According to the interface getSkuDetails, you need to pass in the Set \<product\_Ids> of in-app purchases, the return value is a dictionary, product\_id is key, value: SKProduct object

```swift
Appflow.shared.getSkuDetails(productIds: Set(productIDs)) { skuDetailInfo, error in
    
};
```

**API Reference**

| getSkuDetails |                  |
| ------------- | ---------------- |
| error = nil   | Success callback |
| error != nil  | Failure callback |

| error: Error |               |
| ------------ | ------------- |
| code         | error code    |
| message      | error message |

| Request parameter: data type | Instructions                                                       |
| ---------------------------- | ------------------------------------------------------------------ |
| productIds: Set              | The parameter is the product ID configured in the Apple background |

| Returned parameter: data type      | Instructions                  |
| ---------------------------------- | ----------------------------- |
| skuDetailInfo: \[String:SKProduct] | key - value, Return SKProduct |

After obtaining SKProduct, you can obtain information related to in-app purchases, and developers can save them in their own projects. Subsequent payment needs to be used.\
SKProduct, please refer to the official documentation of Apple

### **Check if payment is available**

```swift
let canMakePurchases = Appflow.shared.canMakePayment()
```

This interface returns data of type 'bool'. True: indicates that payment can be pulled up. False: indicates that payment cannot be pulled up

### **Making Purchase**

To start the purchase process call function purchaseProduct it will take SKProduct object as a parameter.

{% hint style="info" %}
&#x20;<mark style="color:blue;">**NOTE**</mark>

After the in-app purchase is completed, uploadUserInfo must be reported, which is mainly used for user data attribution.

If the developer has integrated the purchase method of AppflowSDK (Appflow\.shared.purchaseSKProduct). There will be no need to process and report user information after subscription, this step has been completed automatically, (versions after V1.0.7 will be automatically reported, and versions before V1.0.7 need to be manually reported by developers)\
If the developer does not integrate the method of purchasing goods in AppflowSDK, the developer will need to manually handle it. After the user completes the purchase, use this method uploadUserInfo to report the user information, where UserId is not a mandatory parameter
{% endhint %}

```swift
// After V1.0.7, it will be automatically reported after subscription
Appflow.shared.purchaseSKProduct(skProduct) { transaction, subscriber, error, canceled in
    if canceled || error != nil {
        print("Canceled")
    } else {
        print("Completed")
    }
}

// Before V1.0.7, the subscription must be reported manually, otherwise it will lead to data loss
Appflow.shared.purchaseSKProduct(skProduct) { transaction, subscriber, error, canceled in
    if canceled || error != nil {
        print("Canceled")
    } else {
        // Manually report user information after subscription is complete
        Appflow.shared.uploadUserInfo()
    }
}
```

{% hint style="info" %} <mark style="color:blue;">**Note**</mark>

Closure will return SKPaymentTransaction, entitlement dictionary, error if it occurred and bool to indicate if a user canceled the purchase process.
{% endhint %}

Users can use **IMSubscriber.isActive** to determine the subscription status. true: subscribed\purchased; false: unsubscribed\unpurchased\
IMSubscriber.expireAt, Return the expiration time of the current subscription. You can compare `expireAt` with the current time and process the subscription status

**API Reference**<br>

| purchaseSKProduct |                                                                                                                        |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------- |
| error = nil       | Success callback                                                                                                       |
| error != nil      | Failure callback, **Note:** A user's subscription status cannot be determined by ERROR.Please use:isActive or expireAt |

| error: Error |               |   |
| ------------ | ------------- | - |
| code         | error code    |   |
| message      | error message |   |

| Request parameter: data type | Instructions        |   |
| ---------------------------- | ------------------- | - |
| product: SKProduct           | Product information |   |

| Returned parameter: data type     | Instructions                                           |
| --------------------------------- | ------------------------------------------------------ |
| transaction: SKPaymentTransaction | An object in the payment queue. \[SKPaymentTransaction |
| isCanceled: Bool                  | Cancel the payment                                     |

| entitlement: IMSubscriber         | instructions                                                                                      |
| --------------------------------- | ------------------------------------------------------------------------------------------------- |
| productId: String                 | product id                                                                                        |
| expireAt: Int64                   | Expiration time of the subscription (millisecond)                                                 |
| isActive: Bool                    | product subscription\purchase status, true: subscribed\purchased; false: unsubscribed\unpurchased |
| imEntitlement: \[IMEntitlement]   | check entitlement for current status                                                              |
| imSubscription: \[IMSubscription] | subscription details by product\_id                                                               |

| IMEntitlement     | Instructions                                                                                      |
| ----------------- | ------------------------------------------------------------------------------------------------- |
| id: String        | product group id                                                                                  |
| expireAt: Int64   | Expiration time of the subscription (millisecond)                                                 |
| isActive: Bool    | product subscription\purchase status, true: subscribed\purchased; false: unsubscribed\unpurchased |
| productId: String | product id                                                                                        |

| IMSubscription             | product Id                                                 |
| -------------------------- | ---------------------------------------------------------- |
| status: IMSubscriberStatus | SubscriptionState\_State                                   |
| expireAt: Int64            | Expiration time of the subscription (millisecond)          |
| cancelAt: Int64            | Time to unsubscribe (millisecond)                          |
| willRenewTo: String        | The next subscription ID to switch: Product ID             |
| originalTransactions:      | \[IMSubscriptionOriginalTransaction] Original transactions |

| IMSubscription       | product Id                                        |
| -------------------- | ------------------------------------------------- |
| originalTxid: String | Original order note                               |
| expireAt: Int64      | Expiration time of the subscription (millisecond) |
| startAt: Int64       | Time to start the subscription (millisecond)      |

### **Checking User Status**

To check if user has any active entitlements call function hasActiveSubscription

```swift
Appflow.shared.hasActiveSubscription({ [weak self] subscriber, error in
    guard let `self` = self else { return }
    var status = "pro not active"
    if error == nil {
        if subscriber.isActive {
            status = "pro active"
        }
        self.statusLabel.text = "Subcription status: \(status)"
    }
})
```

Users can use **IMSubscriber.isActive** to determine the subscription status. true: subscribed\purchased; false: unsubscribed\unpurchased\
IMSubscriber.expireAt, Return the expiration time of the current subscription. You can compare `expireAt` with the current time and process the subscription status

### **Restoring Purchases**

To restore user purchases call restorePurchases function.

```swift
Appflow.shared.restorePurchases { (subscriber, error) in
    if error == nil {
        if subscriber.isActive {
            // Purchase restored and have active entitlment
        }else {
            // Purchase restore finished but user don't have active entitlment
        }
    }
}
```

Users can use **IMSubscriber.isActive** to determine the subscription status. true: subscribed\purchased; false: unsubscribed\unpurchased\
IMSubscriber.expireAt, Return the expiration time of the current subscription. You can compare `expireAt` with the current time and process the subscription status

### **Upload User Info**

Introduction: If you need to associate the userId in your application with the Appflow platform, then the developer needs to choose an appropriate time to report userId to the Appflow platform, for example, when your application logs in. Where useId is the unique identifier defined in the App.

```swift
Appflow.shared.uploadUserInfo(userId: "xxx") { _, _ in   }
```

**expand**

Add a json string field in the UploadUserInfo API to support developers uploading map\<string, string> type data. It supports fixed fields: username, email, phone, gender, age.

* Field:
* All key , value length limit is 128 , calculated according to toString characters
* The fixed fields are username, email, phone, gender, age, please refer to the table below, gender is a number, and the meaning is shown in the table
* There are no restrictions on the type of additional fields added by the user, both numbers and strings can be used
* If you need to report user-related data, report it through the following methods

| field    | type                                    |
| -------- | --------------------------------------- |
| username | string                                  |
| email    | string                                  |
| phone    | string                                  |
| gender   | number(FEMALE = 1, MALE = 2, OTHER = 3) |
| age      | number                                  |

```swift
//uploadUserInfo to extraAttribute
Appflow.shared.uploadUserInfo(userId: "app_user_idxxxxx", extraAttribute: extraAttribute) {[weak self] _, _ in
}
```
