# Use your own paywall

{% hint style="warning" %}
The feature described in this section is supported on the following versions and above:

* iOS: 3.5.0
* Android: 3.5.0
* ReactNative: 2.5.0
* Cordova: 2.5.0
* Flutter: 1.5.0
  {% endhint %}

It could sound weird to use your home made paywall in a No Code paywall environment but it is indeed very useful to:

* A/A test your paywall against the same one implemented using Purchasely's template
* Test your existing paywall against one of our No Code paywall and run tests to beat your reference
* Easily deploy [price A/B testing](https://help.purchasely.io/en/articles/6990589-price-a-b-test) infrastructure without changing your UI

## Implementation

The [basic paywall implementation](https://purchasely.gitbook.io/purchasely/quick-start-1/sdk-configuration/config-appendices/present-paywalls) directly returns a piece of UI but to be able to make both your paywall alongside with Purchasely's paywalls you will need to proceed differently.\
\
First, you must [declare your own paywall](https://help.purchasely.io/en/articles/6940803-your-own-paywall-in-the-purchasely-console) in our console along with the plans you wish to offer on your paywall if you want to do [price A/B tests](https://help.purchasely.io/en/articles/6990589-price-a-b-test)

Then you will need to fetch the paywalls and therefore you won't be able to directly display a screen returned by Purchasely.

You will have to first fetch the paywall then check whether you should display your own paywall or display the provided paywall.\\

{% tabs %}
{% tab title="Swift" %}

```swift
Purchasely.fetchPresentation(for: "onboarding", fetchCompletion: { presentation, error in
            guard let presentation = presentation, error == nil else {
                print("Error while fetching presentation: \(error?.localizedDescription ?? "unknown")")
                return
            }
            
            if presentation.type == .normal || presentation.type == .fallback {
                let paywallController = presentation.controller
                
                // display paywall controller.
                
            } else if presentation.type == .deactivated {
                
                // nothing to display
                
            } else if presentation.type == .client {
                let presentationId = presentation.id
                let plans = presentation.plans
                let metadata = presentation.metadata // look section below
                
                // display your own paywall
                
            }
        })
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
[Purchasely fetchPresentationFor:@"onboarding" fetchCompletion:^(PLYPresentation * _Nullable presentation, NSError * _Nullable error) {
        if (error != nil) {
            NSLog(@"Error while fetching presentation: %@", error.localizedDescription);
            return;
        }
        
        if (presentation.type == PLYPresentationTypeNormal || presentation.type == PLYPresentationTypeFallback) {
            PLYPresentationViewController *paywallController = presentation.controller;
            
            // display paywall controller.
            
        } else if (presentation.type == PLYPresentationTypeDeactivated) {
            
            // nothing to display
            
        } else if (presentation.type == PLYPresentationTypeClient) {
            NSString *presentationId = presentation.id;
            NSArray<NSString *> *plans = presentation.plans;
            
            //display your own paywall
            
        }
    } completion:nil];
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
Purchasely.fetchPresentationForPlacement(this, "onboarding") { presentation, error ->
    if(error != null) {
        Log.d("Purchasely", "Error fetching paywall", error)
        return@fetchPresentationForPlacement
    }

    when(presentation?.type) {
        PLYPresentationType.NORMAL,
        PLYPresentationType.FALLBACK -> {
            val paywallView = presentation.buildView(
                context = this@MainActivity,
                viewProperties = PLYPresentationViewProperties(
                    onClose = {
                        // TODO remove view
                    }
                )
            )
            // Display Purchasely paywall
        }
        PLYPresentationType.DEACTIVATED -> {
            // Nothing to display
        }
        PLYPresentationType.CLIENT -> {
            val paywallId = presentation.id
            val plans = presentation.plans
            val metadata = presentation.metadata // look section below
            
            // Display your own paywall
        }
        else -> {
            //No presentation, it means an error was triggered
        }
    }
}
```

{% endtab %}

{% tab title="Java" %}

<pre class="language-java"><code class="lang-java"><strong>Purchasely.fetchPresentationForPlacement(
</strong>    this, 
    "onboarding", 
    null,
    (presentation, error) -> {
        if(error != null) {
            Log.d("Purchasely", "Error fetching paywall", error);
            return null;
        }
        
        if(presentation.getType() == PLYPresentationType.CLIENT) {
            String paywallId = presentation.getId();
            List&#x3C;String> planIds = presentation.getPlans();
            // Display your own paywall
        }
        
        return null;
<strong>    }
</strong><strong>);
</strong></code></pre>

{% endtab %}

{% tab title="React Native" %}

```typescript
try {
  // Fetch presentation to display
  const presentation = await Purchasely.fetchPresentation({
      placementId: 'onboarding'
  })

  if(presentation.type == PLYPresentationType.CLIENT) {
    // Display my own paywall
    return
  }

} catch (e) {
  console.error(e);
}
```

{% endtab %}

{% tab title="Flutter" %}

```dart
try {
  var presentation = await Purchasely.fetchPresentation("ONBOARDING");

  if (presentation == null) {
    print("No presentation found");
    return;
  }

  if (presentation.type == PLYPresentationType.deactivated) {
    // No paywall to display
    return;
  }

  if (presentation.type == PLYPresentationType.client) {
    // Display my own paywall
    return;
  }

  //Display Purchasely paywall

  var presentResult = await Purchasely.presentPresentation(presentation,
      isFullscreen: false);

  switch (presentResult.result) {
    case PLYPurchaseResult.cancelled:
      {
        print("User cancelled purchased");
      }
      break;
    case PLYPurchaseResult.purchased:
      {
        print("User purchased ${presentResult.plan?.name}");
      }
      break;
    case PLYPurchaseResult.restored:
      {
        print("User restored ${presentResult.plan?.name}");
      }
      break;
  }
} catch (e) {
  print(e);
}
```

{% endtab %}

{% tab title="Cordova" %}

```javascript
// coming soon
```

{% endtab %}
{% endtabs %}

Then call `clientPresentationDisplayed(presentation)` when your paywall is displayed and `clientPresentationClosed(presentation)` when your paywall is closed.

These steps are mandatory for Purchasely to compute conversion on your paywall and measure the performance of A/B tests.

{% tabs %}
{% tab title="Swift" %}

```swift
// Call when your paywall is displayed
// in ViewDidAppear for example
Purchasely.clientPresentationOpened(with: presentation)
            
// Call when your paywall has been closed
// in viewWillDisappear for example
Purchasely.clientPresentationClosed(with: presentation)

// Call clientPresentationClosed in Purchasely.syncPurchase() closure 
// if you are in paywallObserverMode
```

{% endtab %}

{% tab title="Objective-C" %}

```objectivec
// Call when your paywall is displayed
// in ViewDidAppear for example
[Purchasely clientPresentationOpenedWith:presentation];
            
// Call when your paywall has been closed
// in viewWillDisappear for example
[Purchasely clientPresentationClosedWith:presentation];
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// Call when your paywall is displayed
// For example in the onCreate() method of your Activity
Purchasely.clientPresentationDisplayed(presentation)

// Call when your paywall is closed
// For example in the onDestroy() method of your Activity
Purchasely.clientPresentationClosed(presentation)
```

{% endtab %}

{% tab title="Java" %}

```java
// Call when your paywall is displayed
// For example in the onCreate() method of your Activity
Purchasely.clientPresentationDisplayed(presentation);

// Call when your paywall is closed
// For example in the onDestroy() method of your Activity
Purchasely.clientPresentationClosed(presentation);
```

{% endtab %}

{% tab title="React Native" %}

<pre class="language-typescript"><code class="lang-typescript">// Call when your paywall is displayed
<strong>Purchasely.clientPresentationDisplayed(presentation);
</strong>
// Call when your paywall is closed
Purchasely.clientPresentationClosed(presentation);

</code></pre>

{% endtab %}

{% tab title="Flutter" %}

```dart
// Call when your paywall is displayed
Purchasely.clientPresentationDisplayed(presentation);

// Call when your paywall is closed
Purchasely.clientPresentationClosed(presentation);
```

{% endtab %}

{% tab title="Cordova" %}

```javascript
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
You can of course start the purchase from your own paywall with Purchasely by using `Purchasely.purchase(plan)`, more information [here](https://purchasely.gitbook.io/purchasely/advanced-features/manually-trigger-purchases)
{% endhint %}

## Metadata

{% hint style="warning" %}
Available starting with SDK 4.1.0
{% endhint %}

<div><figure><img src="https://2145676854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FKuAJGBnHJWZbqzA4g8yO%2Fuploads%2FnqxX3mVXde1rFpTaRLAp%2FSCR-20231002-pyyu.png?alt=media&#x26;token=6c036ce3-6b1f-45c0-9187-b31260c887a6" alt=""><figcaption></figcaption></figure> <figure><img src="https://2145676854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FKuAJGBnHJWZbqzA4g8yO%2Fuploads%2FzKetGDn3t7M90Ky23eGY%2FSCR-20231002-pziv.png?alt=media&#x26;token=8f80a9e0-c30b-4949-8a1c-7b3dfc8a4d41" alt=""><figcaption></figcaption></figure> <figure><img src="https://2145676854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FKuAJGBnHJWZbqzA4g8yO%2Fuploads%2FGhEJrfzcDm7n9sBKs0Sq%2FSCR-20231002-pzgz.png?alt=media&#x26;token=3ca20a08-b061-4d3a-953c-a832b3501e64" alt=""><figcaption></figcaption></figure> <figure><img src="https://2145676854-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FKuAJGBnHJWZbqzA4g8yO%2Fuploads%2F4tXVFj9539qualMlaEpb%2FSCR-20231002-pzbz.png?alt=media&#x26;token=4c0e481f-b410-444a-b8e6-9c1771fdb96c" alt=""><figcaption><p>Example of Metadata</p></figcaption></figure></div>

You can declare your metadata with Your Own Paywall which can be:

* String (but you can set different types with it, look at example code below)
* Image
* Boolean

These steps are mandatory for Purchasely to compute conversion on your paywall and measure the performance of A/B tests.

{% tabs %}
{% tab title="Swift" %}

```swift
// Documentation coming soon

// Look at Kotlin example as it is the same methods
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
Purchasely.fetchPresentationForPlacement(this, "onboarding") { presentation, error ->
    if(error != null) {
        Log.d("Purchasely", "Error fetching paywall", error)
        return@fetchPresentationForPlacement
    }
    
    if(presentation?.type == PLYPresentationType.CLIENT) {
        val paywallId = presentation.id
        val plans = presentation.plans
        val metadata = presentation.metadata
        
        // Get string metadata, this method is asynchronous as it will parse tags
        val myString = metadata.getString("myString", callback = {
            Log.d("Demo", "myString: $it")
        })
        // You can also use coroutine
        scope.launch {
            val myString = metadata.getString("myString")
        }
        
        // If you do not use Purchasely tags
        val myString2 = metadata.getStringWithoutTags("myString2")
        
        val myBoolean = metadata.getBoolean("myBoolean")

        // Even if declared as a String in our console, 
        // Purchasely SDK will automatically try to convert to specific type 
        // For example: "4.7" is a double or float
        val myInt = metadata.getInt("myInt")
        val myLong = metadata.getLong("myLong")
        val myFloat = metadata.getFloat("myFloat")
        val myDouble = metadata.getDouble("myDouble")
        
        // If you set a metadata with a String ISO 8601, you can retrieve the date
        // "2023-09-21T09:48:25Z"
        val myDate = metadata.getDate("myDate")
        // or Instant
        val myDateAsInstant = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            metadata.getInstant("myDate")
        } else null
        
        // If you set a medata with a String which is a json, you can retrieve it
        // "{"name":"Stranger","int_value":4,"child":{"title":"Things"}}"
        val myJson = metadata.getJsonObject("myJson")
    }
}
```

{% endtab %}

{% tab title="React Native" %}

```typescript
// Release coming soon
```

{% endtab %}

{% tab title="Flutter" %}

```dart
// Release coming soon
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
You can of course do the purchase transaction from your own paywall with Purchasely by using `Purchasely.purchase(plan)`, more information [here](https://purchasely.gitbook.io/purchasely/advanced-features/manually-trigger-purchases)
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://purchasely.gitbook.io/purchasely/advanced-features/use-your-own-paywall.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
