Skip to content
This repository was archived by the owner on Jun 23, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

The changelog for `Superwall`. Also see the [releases](https://github.com/superwall/react-native-superwall/releases) on GitHub.

## 2.1.0 (Beta 1)

### Enhancements

- Adds `didRedeem` and `willRedeem` to support web checkout
- Upgrades iOS SDK to 4.3.7 [View iOS SDK release notes](https://github.com/superwall/Superwall-iOS/releases/tag/4.3.7).


## 2.0.14

### Enhancements
Expand Down
9 changes: 9 additions & 0 deletions example/src/MySuperwallDelegate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
SuperwallEventInfo,
EventType,
} from '../../src';
import type { RedemptionResult } from '../../src/public/RedemptionResults';

export class MySuperwallDelegate extends SuperwallDelegate {
subscriptionStatusDidChange(
Expand Down Expand Up @@ -77,4 +78,12 @@ export class MySuperwallDelegate extends SuperwallDelegate {
): void {
console.log(`[${level}] ${scope}: ${message}`, info, error);
}

willRedeemLink(): void {
console.log('Will redeem link');
}

didRedeemLink(result: RedemptionResult): void {
console.log('Did redeem link:', result);
}
}
8 changes: 8 additions & 0 deletions ios/Bridges/SuperwallDelegateBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,12 @@ final class SuperwallDelegateBridge: SuperwallDelegate {
private func sendEvent(withName name: String, body: [String: Any]) {
SuperwallReactNative.emitter.sendEvent(withName: name, body: body)
}

func willRedeemLink() {
sendEvent(withName: "willRedeemLink", body: [:])
}

func didRedeemLink(withResult result: RedemptionResult) {
sendEvent(withName: "didRedeemLink", body: result.toJson())
}
}
138 changes: 138 additions & 0 deletions ios/Json/RedemptionResult+Json.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//
// RedemptionResult+Json.swift
// SuperwallKit
//
// Created on 14/03/2025.
//

import SuperwallKit

extension RedemptionResult {
func toJson() -> [String: Any] {
var map: [String: Any] = [:]

switch self {
case let .success(code, redemptionInfo):
map["status"] = "SUCCESS"
map["code"] = code
map["redemptionInfo"] = redemptionInfo.toJson()
case let .error(code, error):
map["status"] = "ERROR"
map["code"] = code
map["error"] = error.toJson()
case let .expiredCode(code, info):
map["status"] = "CODE_EXPIRED"
map["code"] = code
map["expired"] = info.toJson()
case .invalidCode(let code):
map["status"] = "INVALID_CODE"
map["code"] = code
case let .expiredSubscription(code, redemptionInfo):
map["status"] = "EXPIRED_SUBSCRIPTION"
map["code"] = code
map["redemptionInfo"] = redemptionInfo.toJson()
}

return map
}
}

extension RedemptionResult.ErrorInfo {
func toJson() -> [String: Any] {
return ["message": self.message]
}
}

extension RedemptionResult.ExpiredCodeInfo {
func toJson() -> [String: Any] {
var map: [String: Any] = [:]

map["resent"] = self.resent
if let obfuscatedEmail = self.obfuscatedEmail {
map["obfuscatedEmail"] = obfuscatedEmail
}

return map
}
}

extension RedemptionResult.RedemptionInfo {
func toJson() -> [String: Any] {
var map: [String: Any] = [:]

map["ownership"] = self.ownership.toJson()
map["purchaserInfo"] = self.purchaserInfo.toJson()
if let paywallInfo = self.paywallInfo {
map["paywallInfo"] = paywallInfo.toJson()
}
map["entitlements"] = self.entitlements.map { $0.toJson() }

return map
}
}

extension RedemptionResult.RedemptionInfo.Ownership {
func toJson() -> [String: Any] {
var map: [String: Any] = [:]

switch self {
case .appUser(let appUserId):
map["type"] = "APP_USER"
map["appUserId"] = appUserId
case .device(let deviceId):
map["type"] = "DEVICE"
map["deviceId"] = deviceId
}

return map
}
}

extension RedemptionResult.RedemptionInfo.PurchaserInfo {
func toJson() -> [String: Any] {
var map: [String: Any] = [:]

map["appUserId"] = self.appUserId
if let email = self.email {
map["email"] = email
}
map["storeIdentifiers"] = self.storeIdentifiers.toJson()

return map
}
}

extension RedemptionResult.RedemptionInfo.PurchaserInfo.StoreIdentifiers {
func toJson() -> [String: Any] {
var map: [String: Any] = [:]

switch self {
case let .stripe(customerId, subscriptionIds):
map["store"] = "STRIPE"
map["stripeCustomerId"] = customerId
map["stripeSubscriptionIds"] = subscriptionIds
case let .unknown(store, additionalInfo):
map["store"] = store
// Add all the additional info to the map
for (key, value) in additionalInfo {
map[key] = value
}
}

return map
}
}

extension RedemptionResult.RedemptionInfo.PaywallInfo {
func toJson() -> [String: Any] {
var map: [String: Any] = [:]

map["identifier"] = self.identifier
map["placementName"] = self.placementName
map["placementParams"] = self.placementParams
map["variantId"] = self.variantId
map["experimentId"] = self.experimentId

return map
}
}
2 changes: 2 additions & 0 deletions ios/SuperwallReactNative.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class SuperwallReactNative: RCTEventEmitter {
"purchaseFromAppStore",
"purchaseFromGooglePlay",
"paywallWillOpenURL",
"willRedeemLink",
"didRedeemLink",
"restore",
"paywallPresentationHandler",
"subscriptionStatusDidChange",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@superwall/react-native-superwall",
"version": "2.0.14",
"version": "2.1.0-beta.1",
"description": "The React Native package for Superwall",
"main": "lib/commonjs/index",
"module": "lib/module/index",
Expand Down
10 changes: 10 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type { PresentationResult } from './public/PresentationResult';
import { fromJson as paywallResultFromJson } from './public/PaywallResult';
import { EntitlementsInfo } from './public/EntitlementsInfo';
import type { LogLevel } from './public/LogLevel';
import { RedemptionResults } from './public/RedemptionResults';
const { version } = require('../package.json');

const LINKING_ERROR =
Expand Down Expand Up @@ -249,6 +250,15 @@ export default class Superwall {
const url = new URL(data.url);
Superwall.delegate?.paywallWillOpenURL(url);
});

this.eventEmitter.addListener('willRedeemLink', async () => {
Superwall.delegate?.willRedeemLink();
});

this.eventEmitter.addListener('didRedeemLink', async (data) => {
const result = RedemptionResults.fromJson(data.result);
Superwall.delegate?.didRedeemLink(result);
});
}

private async observeSubscriptionStatus() {
Expand Down
Loading
Loading