Customer Engagement Platform for Email, Push Notifications, & SMS
모바일 푸시, 웹 푸시, SMS, 이메일, 인앱 메시징 분야 세계 최고, 200만 기업이 신뢰해 연간 120억 건 이상 전송
구독 기반으로 창작자에게 지급하고, 링크 추적·대시보드·RevenueCat 웹훅을 제공하며 ATT 프롬프트가 없습니다
Pay creators on the subscriptions they drive, not per post. Tracked links, creator dashboards, RevenueCat webhooks and no ATT prompt. Free to start.
마케팅TypeScript · JavaGitHub @tapapplink온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Tap App Link React Native SDK — install attribution for Expo and React Native
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Attribution SDK for React Native apps, including Expo. Call configure, trackInstall, and setAppUserId with the same billing user id your webhook provider sends.
Works in Expo dev clients and production builds. Expo Go cannot load the Android Play Install Referrer module.
Install 0.3.2 or later of the SDK, together with AsyncStorage. AsyncStorage is required so install state survives cold launches; without it each launch can post a new install.
Bare React Native iOS apps should run cd ios && pod install after installing.
Rebuild the native binary after adding the package.README 전문 보기 ↗Attribution SDK for React Native apps, including Expo. Call configure, trackInstall, and setAppUserId with the same billing user id your webhook provider sends.
Works in Expo dev clients and production builds. Expo Go cannot load the Android Play Install Referrer module.
Install 0.3.3 or later of the SDK, together with AsyncStorage. AsyncStorage is required so install state survives cold launches; without it each launch can post a new install.
Expo
npx expo install @tapapplink/[email protected] @react-native-async-storage/async-storage
Bare React Native
npm install @tapapplink/[email protected] @react-native-async-storage/async-storage
# or: yarn add @tapapplink/[email protected] @react-native-async-storage/async-storage
Bare React Native iOS apps should run cd ios && pod install after installing.
Rebuild the native binary after adding the package.
The Play Install Referrer bridge is Android-only. Storing install state only there would leave iOS (and Expo iOS builds) without persistence. AsyncStorage is the usual cross-platform key/value store for Expo and bare React Native, so one path covers both platforms.
import {
TapAppLink,
TapAppLinkRedeemError,
} from "@tapapplink/react-native";
TapAppLink.configure({
publicKey: "etk_live_…",
environment: __DEV__ ? "sandbox" : "production",
debug: __DEV__,
});
await TapAppLink.trackInstall();
await TapAppLink.setAppUserId(await Purchases.getAppUserID());
const offer = TapAppLink.getOffer();
trackInstall posts /ingestInstall once per install (keyed by a persisted installId). Later launches restore the stored attribution id and offer without posting again. setAppUserId sends that stored attributionId after hydrate, posts /ingestIdentify only when the app user id changes, and coalesces concurrent identical calls.
Set debug: true to log each request, response and stored state. The API key is redacted in those logs.
Purchases are attributed with billing webhooks, not a client trackPurchase call.
Show success only when applyCode resolves. Map each result to UI state as below. Customers must never see the word "environment".
On a network (or timeout) error only, retry applyCode once with the same code. Do not auto-retry unknownCode, inactiveCode, or wrongEnvironment. If that retry also fails, show N6 ("We couldn't check your code. Check your connection and try again.") with a Try again button. From 0.3.2, that automatic retry is safe because the SDK reuses its request ID, so the server will not count the install twice. From 0.3.3, success results may include optional codeMatches and offer.creatorName.
type RedeemUi = {
title: string;
detail?: string;
hint?: string;
showCode: boolean;
/** When true, show a Try again button (N6 after a failed network retry). */
tryAgain?: boolean;
};
async function redeemForUi(code: string): Promise<RedeemUi> {
const offerLineFrom = (result: Record<string, unknown>): string | undefined => {
const offer = result.offer;
if (
offer &&
typeof offer === "object" &&
typeof (offer as { creatorName?: string }).creatorName === "string"
) {
return `Offer from ${(offer as { creatorName: string }).creatorName}`;
}
return undefined;
};
const mapSuccess = (result: Record<string, unknown>): RedeemUi => {
const offerLine = offerLineFrom(result);
if (result.alreadyAttributed === true) {
return {
title: "You're all set",
detail: offerLine,
showCode: false,
};
}
return {
title: "Code applied",
detail: offerLine,
showCode: true,
};
};
const networkFailureUi = (): RedeemUi => ({
title: "We couldn't check your code. Check your connection and try again.",
showCode: true,
tryAgain: true,
});
const mapError = (error: unknown): RedeemUi => {
if (error instanceof TapAppLinkRedeemError) {
switch (error.code) {
case "unknownCode":
return {
title: "We don't recognise that code. Check it and try again.",
hint: "Codes aren't case sensitive.",
showCode: true,
};
case "inactiveCode":
return {
title: "This code is no longer active.",
hint: "You can still subscribe at the regular price.",
showCode: true,
};
case "wrongEnvironment":
// Developer only. Customers see the unknownCode copy (never "environment").
console.warn(TapAppLinkRedeemError.wrongEnvironmentDevWarning);
return {
title: "We don't recognise that code. Check it and try again.",
hint: "Codes aren't case sensitive.",
showCode: true,
};
case "network":
return networkFailureUi();
case "other":
default:
console.warn("TapAppLink applyCode failed", {
code: error.code,
status: error.status,
message: error.message,
});
return networkFailureUi();
}
}
throw error;
};
try {
return mapSuccess(await TapAppLink.applyCode(code));
} catch (error) {
// Auto-retry once for network/timeout only. Never for unknown/inactive/wrongEnvironment.
if (error instanceof TapAppLinkRedeemError && error.code === "network") {
try {
return mapSuccess(await TapAppLink.applyCode(code));
} catch (retryError) {
if (
retryError instanceof TapAppLinkRedeemError &&
retryError.code === "network"
) {
return networkFailureUi();
}
return mapError(retryError);
}
}
return mapError(error);
}
}
Maintainers: see PUBLISHING.md for the CI release tag flow and one-time npm trusted publishing setup.
MIT