Using No-Codes with custom purchases handling

By default, Qonversion No-Codes utilize the Qonversion SDK for managing purchases. This implies that you should operate Qonversion in subscription management mode. However, if you have your own purchasing infrastructure, you can manage purchases and restores from No-Code screens on your end. It’s as straightforward as supplying a single delegate to Qonversion No-Codes prior to showing the screen.

Integrating Qonversion No-Codes with your own purchasing infrastructure necessitates:

  • creating a product within the Qonversion dashboard and associating it with a purchase action on your screen,
  • implementing and providing a PurchaseDelegate to Qonversion No-Codes that is responsible for managing purchase and restore requests from No-Code screens.
🚧

Unity warning

On Android, when a No-Codes screen is displayed, Unity's game loop is paused because the No-Codes screen runs as a separate Activity on top of Unity's Activity. This means that this delegate will only work on iOS, and will not work on Android.

Implementing PurchaseDelegate

To handle purchase and restore requests from No-Code screens on your end, you should implement PurchaseDelegate. It has two simple methods - purchase, accepting the requested product, and restore.

extension MyPurchaseHandler: NoCodesPurchaseDelegate {
  func purchase(product: Qonversion.Product) async throws {
    // Execute the purchase as you wish here
    // You can throw exceptions in case of errors
  }
  
  func restore() async throws {
    // Execute the restore as you wish here
    // You can throw exceptions in case of errors
  }
}
// You can use either coroutine interface as follows:
class MyPurchasesHandler : PurchaseDelegate {
    override suspend fun purchase(product: QProduct) {
        // Execute the purchase as you wish here
        // You can throw exceptions in case of errors
    }

    override suspend fun restore() {
        // Execute the restore as you wish here
        // You can throw exceptions in case of errors
    }
}

// Or you can use callbacks interface:
class MyPurchaseHandler : PurchaseDelegateWithCallbacks {
    override fun purchase(
        product: QProduct,
        onSuccess: PurchaseDelegateWithCallbacks.OnSuccess,
        onError: PurchaseDelegateWithCallbacks.OnError
    ) {
        // Execute the purchase as you wish here
        // And call onSuccess once everything is done to close the No-Code screen
        onSuccess()
        // Call onError with exception in case of error.
    }

    override fun restore(
        onSuccess: PurchaseDelegateWithCallbacks.OnSuccess,
        onError: PurchaseDelegateWithCallbacks.OnError
    ) {
        // Execute the restore as you wish here
        // And call onSuccess once everything is done to close the No-Code screen
        onSuccess()
        // Call onError with exception in case of error.
    }
}
class MyPurchasesHandler implements PurchaseDelegateWithCallbacks {
    @Override
    public void purchase(@NonNull QProduct product, @NonNull OnSuccess onSuccess, @NonNull OnError onError) {
        // Execute the purchase as you wish here
        // And call onSuccess once everything is done to close the No-Code screen
        onSuccess.invoke();
        // Call onError with exception in case of error.
    }

    @Override
    public void restore(@NonNull OnSuccess onSuccess, @NonNull OnError onError) {
        // Execute the restore as you wish here
        // And call onSuccess once everything is done to close the No-Code screen
        onSuccess.invoke()
        // Call onError with exception in case of error.
    }
}
class MyPurchaseDelegate implements PurchaseDelegate {
  async purchase(product: Product): Promise<void> {
    // Execute the purchase as you wish here
    // You can throw exceptions in case of errors
  }

  async restore(): Promise<void> {
    // Execute the restore as you wish here
    // You can throw exceptions in case of errors
  }
}
class MyPurchaseDelegate implements NoCodesPurchaseDelegate {
  @override
  Future<void> purchase(QProduct product) async {
    // Execute the purchase as you wish here
    // You can throw exceptions in case of errors
  }

  @override
  Future<void> restore() async {
    // Execute the restore as you wish here
    // You can throw exceptions in case of errors
  }
}
public class MyPurchaseDelegate : NoCodesPurchaseDelegate
{
    public void Purchase(Product product, System.Action onSuccess, System.Action<string> onError)
    {
        // Execute the purchase as you wish here
        // Call onSuccess() when purchase completes successfully
        // Call onError("error message") in case of errors
        
        try
        {
            // Your purchase logic here
            onSuccess();
        }
        catch (System.Exception e)
        {
            onError(e.Message);
        }
    }

    public void Restore(System.Action onSuccess, System.Action<string> onError)
    {
        // Execute the restore as you wish here
        // Call onSuccess() when restore completes successfully
        // Call onError("error message") in case of errors
        
        try
        {
            // Your restore logic here
            onSuccess();
        }
        catch (System.Exception e)
        {
            onError(e.Message);
        }
    }
}
class MyPurchaseDelegate implements Qonversion.PurchaseDelegate {
  async purchase(product: Qonversion.Product): Promise<void> {
    // Execute the purchase as you wish here
    // You can throw exceptions in case of errors
  }

  async restore(): Promise<void> {
    // Execute the restore as you wish here
    // You can throw exceptions in case of errors
  }
}
class MyPurchaseDelegate implements PurchaseDelegate {
  async purchase(product: Product): Promise<void> {
    // Execute the purchase as you wish here
    // You can throw exceptions in case of errors
  }

  async restore(): Promise<void> {
    // Execute the restore as you wish here
    // You can throw exceptions in case of errors
  }
}

Providing PurchaseDelegate

Once you've implemented the PurchaseDelegate, you need to provide it to Qonversion No-Codes. There are two equal ways to do it - during the initialization via configuration or after - via the No-Codes method.

// Providing during the initialization
let purchaseDelegate = MyPurchaseHandler()
let configuration = NoCodesConfiguration(projectKey: "projectKey", purchaseDelegate: purchaseDelegate)
NoCodes.initialize(with: configuration)

// Or after initialization
let purchaseDelegate = MyPurchaseHandler()
NoCodes.shared.set(purchaseDelegate: purchaseDelegate)
// Providing during the initialization
val purchaseDelegate = MyPurchaseHandler()
val noCodesConfig = NoCodesConfig.Builder(this, "projectKey")
    .setPurchaseDelegate(purchaseDelegate)
    .build()
NoCodes.initialize(noCodesConfig)

// Or after initialization
val purchaseDelegate = MyPurchaseHandler()
NoCodes.shared.setPurchaseDelegate(purchaseDelegate)
// Providing during the initialization
final PurchaseDelegateWithCallbacks purchaseDelegate = new MyPurchaseHandler();
final NoCodesConfig noCodesConfig = new NoCodesConfig.Builder(this, "projectKey")
    .setPurchaseDelegate(purchaseDelegate)
    .build();
NoCodes.initialize(noCodesConfig);

// Or after initialization
final PurchaseDelegateWithCallbacks purchaseDelegate = new MyPurchaseHandler();
NoCodes.getSharedInstance().setPurchaseDelegate(purchaseDelegate);
// Providing during the initialization
const purchaseDelegate = MyPurchaseHandler()
const noCodesConfig = new NoCodesConfigBuilder('projectKey')
  .setPurchaseDelegate(purchaseDelegate)
  .build();
NoCodes.initialize(noCodesConfig);

// Or after initialization
const purchaseDelegate = MyPurchaseHandler()
NoCodes.getSharedInstance().setPurchaseDelegate(purchaseDelegate);
// Providing during the initialization
final purchaseDelegate = MyPurchaseDelegate();
final noCodesConfig = NoCodesConfigBuilder('projectKey')
    .setPurchaseDelegate(purchaseDelegate)
    .build();
NoCodes.initialize(noCodesConfig);

// Or after initialization
final purchaseDelegate = MyPurchaseDelegate();
await NoCodes.getSharedInstance().setPurchaseDelegate(purchaseDelegate);
// Providing during the initialization
var purchaseDelegate = new MyPurchaseDelegate();
var noCodesConfig = new NoCodesConfigBuilder("projectKey")
    .SetPurchaseDelegate(purchaseDelegate)
    .Build();
NoCodes.Initialize(noCodesConfig);

// Or after initialization
var purchaseDelegate = new MyPurchaseDelegate();
NoCodes.GetSharedInstance().SetPurchaseDelegate(purchaseDelegate);
// Providing during the initialization
const purchaseDelegate = MyPurchaseHandler()
const noCodesConfig = new Qonversion.NoCodesConfigBuilder('projectKey')
  .setPurchaseDelegate(purchaseDelegate)
  .build();
Qonversion.NoCodes.initialize(noCodesConfig);

// Or after initialization
const purchaseDelegate = MyPurchaseHandler()
Qonversion.NoCodes.getSharedInstance().setPurchaseDelegate(purchaseDelegate);
// Providing during the initialization
const purchaseDelegate = MyPurchaseHandler()
const noCodesConfig = new NoCodesConfigBuilder('projectKey')
  .setPurchaseDelegate(purchaseDelegate)
  .build();
NoCodes.initialize(noCodesConfig);

// Or after initialization
const purchaseDelegate = MyPurchaseHandler()
NoCodes.getSharedInstance().setPurchaseDelegate(purchaseDelegate);

Availability

The described feature is available in the following SDK versions

PlatformSDK version
iOS6.1.0+
AndroidNo-Codes 1.2.1+
React Native10.1.0+
Flutter11.1.0+
Unity9.0.0+
Cordova7.0.0+
Capacitor1.0.0+