{"_id":"@biltme/iap","_rev":"9-b4310de0adec6a511e6772a2ae150b15","name":"@biltme/iap","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@biltme/iap","version":"0.1.0","_id":"@biltme/iap@0.1.0","maintainers":[{"name":"biltmeadmin","email":"npm@bilt.me"},{"name":"kevinklmsk","email":"kevin@klmsk.dev"}],"dist":{"shasum":"68b008b8f874f4398575d11664f5777539a47cda","tarball":"https://registry.npmjs.org/@biltme/iap/-/iap-0.1.0.tgz","fileCount":42,"integrity":"sha512-KYiSSNfNtcTnUUG3BkChS/Dw+xdu5I4ZiXJXAifgApLHbYU3NHRq2vsq8A9rUStOrOAXN95a0fha6YVYrOYjXw==","signatures":[{"sig":"MEYCIQCrcqDvLkxZumamqnYKyjDWUg8sS8OjcQQQA9+LqE0jcQIhAIxckWIJAUeb3Bk6TDJJBaTdyoo6vbSLKPiXhGnO0kq7","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":148422},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.js","gitHead":"b913c7c37c5cac6ece82a0015193cbde7133c789","scripts":{"build":"tsc","typecheck":"tsc --noEmit","pack:check":"npm pack --dry-run","prepublishOnly":"bun run typecheck && bun run build"},"_npmUser":{"name":"kevinklmsk","email":"kevin@klmsk.dev"},"_npmVersion":"11.12.1","description":"Thin Expo / React Native client wrapper for the `bilt-billing` backend.","directories":{},"sideEffects":false,"_nodeVersion":"25.9.0","react-native":"src/index.ts","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"19.1.0","expo-iap":"^3.1.0","typescript":"^5.9.2","@types/react":"^19.1.8","react-native":"0.81.0","@react-native-async-storage/async-storage":"^2.1.0"},"peerDependencies":{"react":">=18.0.0","expo-iap":">=3.0.0","react-native":">=0.72.0","@react-native-async-storage/async-storage":">=2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/iap_0.1.0_1776717288437_0.7675952274945739","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@biltme/iap","version":"0.1.1","keywords":["bilt","billing","payments","iap","in-app-purchase","storekit","expo","expo-iap","react-native","subscriptions","entitlements"],"license":"MIT","_id":"@biltme/iap@0.1.1","maintainers":[{"name":"biltmeadmin","email":"npm@bilt.me"},{"name":"kevinklmsk","email":"kevin@klmsk.dev"}],"dist":{"shasum":"231e2dd8d6192b8ba88eaa6f8d3985a10a9b5ab7","tarball":"https://registry.npmjs.org/@biltme/iap/-/iap-0.1.1.tgz","fileCount":42,"integrity":"sha512-oIIVpHHfnjt4fehXLF+i+01tuAAMBRNs7xqUDwnQZG66O1aCJxSyGNZw2G1xb/KvkQrv2YY2aJ2Iz7prm4AkZw==","signatures":[{"sig":"MEYCIQChm4jwR8QFslr3jUxR6GRGpF1NUvxmyY8essThyJPN3gIhAJPMl4evb9pbjP2TSW3N/awZ03PqaTU8Y7SedR+Ul7Lg","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":171928},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.js","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"9f54b284f4b7bb99c946cffb6cad058a538709ac","scripts":{"build":"bun run clean && tsc","clean":"rm -rf dist","typecheck":"tsc --noEmit","pack:check":"npm pack --dry-run","prepublishOnly":"bun run typecheck && bun run build"},"_npmUser":{"name":"kevinklmsk","email":"kevin@klmsk.dev"},"_npmVersion":"11.12.1","description":"Expo client for communicating with Bilt payments service. Handles the full iOS in-app purchase lifecycle (bootstrap, purchase, restore, entitlements, offline retry) against a tenant-based billing API.","directories":{},"sideEffects":false,"_nodeVersion":"25.9.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"19.1.0","expo-iap":"^3.1.0","typescript":"^5.9.2","@types/react":"^19.1.8","react-native":"0.81.0","@react-native-async-storage/async-storage":"^2.1.0"},"peerDependencies":{"react":">=18.0.0","expo-iap":">=3.0.0","react-native":">=0.72.0","@react-native-async-storage/async-storage":">=2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/iap_0.1.1_1776777466289_0.009787060014609716","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@biltme/iap","version":"0.1.2","keywords":["bilt","billing","payments","iap","in-app-purchase","storekit","expo","expo-iap","react-native","subscriptions","entitlements"],"license":"MIT","_id":"@biltme/iap@0.1.2","maintainers":[{"name":"biltmeadmin","email":"npm@bilt.me"},{"name":"kevinklmsk","email":"kevin@klmsk.dev"}],"dist":{"shasum":"ab819786accea97b52de8c8b36de549dd1336bbd","tarball":"https://registry.npmjs.org/@biltme/iap/-/iap-0.1.2.tgz","fileCount":42,"integrity":"sha512-dv5UzPh81A4or74X6sp7SJ4h+6mX8GSJKJh+0unNiVLzjgMSVaZB+83GFdYfWxqGcLIMVKpeJGXorZ0maJc2UQ==","signatures":[{"sig":"MEUCIQCrrbmAeDqmqBDhtCfw6gIa8bGJMhkohKxWWhMNGQEbSAIgdQoDRyKNo6azfuTcBt0e6zlQyyRSRFYNE8v7l0vnPEc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":173022},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.js","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"47b6a7a7b1419f5146151518ae4f929a35c26a2b","scripts":{"build":"bun run clean && tsc","clean":"rm -rf dist","typecheck":"tsc --noEmit","pack:check":"npm pack --dry-run","prepublishOnly":"bun run typecheck && bun run build"},"_npmUser":{"name":"kevinklmsk","email":"kevin@klmsk.dev"},"_npmVersion":"11.12.1","description":"Expo client for communicating with Bilt payments service. Handles the full iOS in-app purchase lifecycle (bootstrap, purchase, restore, entitlements, offline retry) against a tenant-based billing API.","directories":{},"sideEffects":false,"_nodeVersion":"25.9.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"19.1.0","expo-iap":"^3.1.0","typescript":"^5.9.2","@types/react":"^19.1.8","react-native":"0.81.0","@react-native-async-storage/async-storage":"^2.1.0"},"peerDependencies":{"react":">=18.0.0","expo-iap":">=3.0.0","react-native":">=0.72.0","@react-native-async-storage/async-storage":">=2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/iap_0.1.2_1776790831642_0.3105755724485595","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@biltme/iap","version":"0.1.3","keywords":["billing","bilt","entitlements","expo","expo-iap","iap","in-app-purchase","payments","react-native","storekit","subscriptions"],"license":"MIT","_id":"@biltme/iap@0.1.3","maintainers":[{"name":"biltmeadmin","email":"npm@bilt.me"},{"name":"kevinklmsk","email":"kevin@klmsk.dev"}],"dist":{"shasum":"2e908b7067bf3bcc171a080c1d57ffa604f9d586","tarball":"https://registry.npmjs.org/@biltme/iap/-/iap-0.1.3.tgz","fileCount":42,"integrity":"sha512-G2WMedf1FC1TEcwOmjaMR3cNm+1gNV+3g9e0VsHn3QzewZv6K49hkE0AtVXYIPd+SkXGraK/unnCADK7X/nbAw==","signatures":[{"sig":"MEYCIQDL5JU9OsiQw4v1P31sKe02H0syPFOe6wOBJx0nT9PPvwIhAKREYklH1aOgCWhBBIO6r6f0QKYH2F9SOdRsYFiVd8T+","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":180705},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.js","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"b269d516b42a3111b40b0e5497000edb83f41a1e","scripts":{"knip":"knip","lint":"bun run lint:ci","build":"bun run clean && tsc","clean":"rm -rf dist","format":"oxfmt --write src/ package.json","lint:ci":"oxlint -c oxlint.config.js src/","typecheck":"tsc --noEmit","pack:check":"npm pack --dry-run","format:check":"oxfmt --check src/ package.json","prepublishOnly":"bun run format:check && bun run lint:ci && bun run knip && bun run build"},"_npmUser":{"name":"kevinklmsk","email":"kevin@klmsk.dev"},"_npmVersion":"11.12.1","description":"Expo client for communicating with Bilt payments service. Handles the full iOS in-app purchase lifecycle (bootstrap, purchase, restore, entitlements, offline retry) against a tenant-based billing API.","directories":{},"sideEffects":false,"_nodeVersion":"25.9.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"knip":"^5.88.1","oxfmt":"^0.44.0","react":"19.1.0","oxlint":"^1.59.0","expo-iap":"^3.1.0","typescript":"^5.9.2","@types/react":"^19.1.8","react-native":"0.81.0","oxlint-tsgolint":"^0.20.0","oxlint-config-universe":"^0.0.3","@react-native-async-storage/async-storage":"^2.1.0"},"peerDependencies":{"react":">=18.0.0","expo-iap":">=3.0.0","react-native":">=0.72.0","@react-native-async-storage/async-storage":">=2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/iap_0.1.3_1776875298599_0.5705402526073309","host":"s3://npm-registry-packages-npm-production"}},"0.1.4":{"name":"@biltme/iap","version":"0.1.4","keywords":["billing","bilt","entitlements","expo","expo-iap","iap","in-app-purchase","payments","react-native","storekit","subscriptions"],"license":"MIT","_id":"@biltme/iap@0.1.4","maintainers":[{"name":"biltmeadmin","email":"npm@bilt.me"},{"name":"kevinklmsk","email":"kevin@klmsk.dev"}],"dist":{"shasum":"9d2f9349008b8ed22671c585d338060584fe807d","tarball":"https://registry.npmjs.org/@biltme/iap/-/iap-0.1.4.tgz","fileCount":46,"integrity":"sha512-wRJnD0SXsvmhYDi8q/blaVM3Lcct4ACf9pu4P4NMBSdLNDfLLLmbvCghWMTTZM3W6p9hZlXBxagO7ttpOz+aZQ==","signatures":[{"sig":"MEYCIQC5QtQNRrOgS6lFMipvwPwfwMQaBDbRlB8+7wFW3Ool3QIhAJwgOSOr3yxt9Rek8iN3Cm/QG17E5WsfOvdTF5b6P6H5","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":186415},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.js","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"39a5afd926427825adb2f01f57b2766d08fefeeb","scripts":{"knip":"knip","lint":"bun run lint:ci","build":"bun run clean && tsc","clean":"rm -rf dist","format":"oxfmt --write src/ package.json","lint:ci":"oxlint -c oxlint.config.js src/","typecheck":"tsc --noEmit","pack:check":"npm pack --dry-run","format:check":"oxfmt --check src/ package.json","prepublishOnly":"bun run format:check && bun run lint:ci && bun run knip && bun run build"},"_npmUser":{"name":"kevinklmsk","email":"kevin@klmsk.dev"},"_npmVersion":"11.12.1","description":"Expo client for communicating with Bilt payments service. Handles the full iOS in-app purchase lifecycle (bootstrap, purchase, restore, entitlements, offline retry) against a tenant-based billing API.","directories":{},"sideEffects":false,"_nodeVersion":"25.9.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"knip":"^5.88.1","oxfmt":"^0.44.0","react":"19.1.0","oxlint":"^1.59.0","expo-iap":"^3.1.0","typescript":"^5.9.2","@types/react":"^19.1.8","react-native":"0.81.0","oxlint-tsgolint":"^0.20.0","oxlint-config-universe":"^0.0.3","@react-native-async-storage/async-storage":"^2.1.0"},"peerDependencies":{"react":">=18.0.0","expo-iap":">=3.0.0","react-native":">=0.72.0","@react-native-async-storage/async-storage":">=2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/iap_0.1.4_1777296024056_0.342384455175496","host":"s3://npm-registry-packages-npm-production"}},"0.1.5":{"name":"@biltme/iap","version":"0.1.5","keywords":["billing","bilt","entitlements","expo","expo-iap","iap","in-app-purchase","payments","react-native","storekit","subscriptions"],"license":"MIT","_id":"@biltme/iap@0.1.5","maintainers":[{"name":"biltmeadmin","email":"npm@bilt.me"},{"name":"kevinklmsk","email":"kevin@klmsk.dev"}],"dist":{"shasum":"0d6c94ebe42fe32aef1d003fdfecdfd3cf66b3ff","tarball":"https://registry.npmjs.org/@biltme/iap/-/iap-0.1.5.tgz","fileCount":46,"integrity":"sha512-TW2QIOMxNcizoTIqrOAPIiBqM2vpeDenpbcCUFlbML8aBw/IZRxe6CabEl1gHWzz9ttjjhnAgBSFRzp4AVQwkg==","signatures":[{"sig":"MEYCIQDa/6YV3d5EKkac2dIzAPCRDxGdRhr3croALNr0U19wIAIhAPdpRnzlm6MHH+Reqy9X2D8x9xck6RP+pHrOSINez97a","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":186415},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.js","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"39a5afd926427825adb2f01f57b2766d08fefeeb","scripts":{"knip":"knip","lint":"bun run lint:ci","build":"bun run clean && tsc","clean":"rm -rf dist","format":"oxfmt --write src/ package.json","lint:ci":"oxlint -c oxlint.config.js src/","typecheck":"tsc --noEmit","pack:check":"npm pack --dry-run","format:check":"oxfmt --check src/ package.json","prepublishOnly":"bun run format:check && bun run lint:ci && bun run knip && bun run build"},"_npmUser":{"name":"kevinklmsk","email":"kevin@klmsk.dev"},"_npmVersion":"11.12.1","description":"Expo client for communicating with Bilt payments service. Handles the full iOS in-app purchase lifecycle (bootstrap, purchase, restore, entitlements, offline retry) against a tenant-based billing API.","directories":{},"sideEffects":false,"_nodeVersion":"25.9.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"knip":"^5.88.1","oxfmt":"^0.44.0","react":"19.1.0","oxlint":"^1.59.0","expo-iap":"^3.1.0","typescript":"^5.9.2","@types/react":"^19.1.8","react-native":"0.81.0","oxlint-tsgolint":"^0.20.0","oxlint-config-universe":"^0.0.3","@react-native-async-storage/async-storage":"^2.1.0"},"peerDependencies":{"react":">=18.0.0","expo-iap":">=3.0.0","react-native":">=0.72.0","@react-native-async-storage/async-storage":">=2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/iap_0.1.5_1777301808341_0.5282179771876403","host":"s3://npm-registry-packages-npm-production"}},"0.1.6":{"name":"@biltme/iap","version":"0.1.6","keywords":["billing","bilt","entitlements","expo","expo-iap","iap","in-app-purchase","payments","react-native","storekit","subscriptions"],"license":"MIT","_id":"@biltme/iap@0.1.6","maintainers":[{"name":"biltmeadmin","email":"npm@bilt.me"},{"name":"kevinklmsk","email":"kevin@klmsk.dev"}],"dist":{"shasum":"97c2dd3284461587e9957db9c17e3b4077c33c68","tarball":"https://registry.npmjs.org/@biltme/iap/-/iap-0.1.6.tgz","fileCount":4,"integrity":"sha512-tNbOalC1xDk9TzpFK2QCqyhh1v3zhKbIx3YRJAFg3TKI94tj6eZwAD/9WVO4r1Yg1nJ3F8ykxmUFT4lfHxw9NQ==","signatures":[{"sig":"MEUCIHrbpleDM3dgKbnhB6aVI3SoWWivwgAspgpUFucyvr3EAiEA6ZtfzIzBs4PHGSMFI/4FEFWpxVrZn5mRIGCzzrvZxyM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":69698},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","module":"dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"c6753e541b1ce8c816e9fdf72f05cfb17b5df5bd","scripts":{"knip":"knip","lint":"bun run lint:ci","build":"bun run clean && tsup","clean":"rm -rf dist","format":"oxfmt --write src/ package.json","lint:ci":"oxlint -c oxlint.config.cjs src/","typecheck":"tsc --noEmit","pack:check":"npm pack --dry-run","format:check":"oxfmt --check src/ package.json","prepublishOnly":"bun run format:check && bun run lint:ci && bun run knip && bun run build"},"_npmUser":{"name":"kevinklmsk","email":"kevin@klmsk.dev"},"_npmVersion":"11.12.1","description":"Expo client for communicating with Bilt payments service. Handles the full iOS in-app purchase lifecycle (bootstrap, purchase, restore, entitlements, offline retry) against a tenant-based billing API.","directories":{},"sideEffects":false,"_nodeVersion":"25.9.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"knip":"^5.88.1","tsup":"^8.5.0","oxfmt":"^0.44.0","react":"19.1.0","oxlint":"^1.59.0","expo-iap":"^3.1.0","typescript":"^5.9.2","@types/react":"^19.1.8","react-native":"0.81.0","oxlint-tsgolint":"^0.20.0","oxlint-config-universe":"^0.0.3","@react-native-async-storage/async-storage":"^2.1.0"},"peerDependencies":{"react":">=18.0.0","expo-iap":">=3.0.0","react-native":">=0.72.0","@react-native-async-storage/async-storage":">=2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/iap_0.1.6_1777564201807_0.8161595034826623","host":"s3://npm-registry-packages-npm-production"}},"0.1.7":{"name":"@biltme/iap","version":"0.1.7","keywords":["billing","bilt","entitlements","expo","expo-iap","iap","in-app-purchase","payments","react-native","storekit","subscriptions"],"license":"MIT","_id":"@biltme/iap@0.1.7","maintainers":[{"name":"biltmeadmin","email":"npm@bilt.me"},{"name":"kevinklmsk","email":"kevin@klmsk.dev"}],"dist":{"shasum":"33db4402ed65e1202425b6346e5d6251fbf09240","tarball":"https://registry.npmjs.org/@biltme/iap/-/iap-0.1.7.tgz","fileCount":4,"integrity":"sha512-ebvf+zs78lOirCkwPhgWsX5U26aLsu92ak5v4O0P9q3J/jjxIJjAlj1g+10VTcbe0zLzLM1QtDUTiv75DLFg3A==","signatures":[{"sig":"MEQCIC4PTAL48i6Zj3WFi3JwYc9qQVzqkcGVV0QnW1R50vlIAiA4ANoaNW8a8JdKdCO9ER/lloGgFnRB9rD6Ag6OXT6b3A==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":69686},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","module":"dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"0e1f4dca0434e6ec9cda8ecad194f60c29854681","scripts":{"knip":"knip","lint":"bun run lint:ci","build":"bun run clean && tsup","clean":"rm -rf dist","format":"oxfmt --write src/ package.json","lint:ci":"oxlint -c oxlint.config.cjs src/","typecheck":"tsc --noEmit","pack:check":"npm pack --dry-run","format:check":"oxfmt --check src/ package.json","prepublishOnly":"bun run format:check && bun run lint:ci && bun run knip && bun run build"},"_npmUser":{"name":"kevinklmsk","email":"kevin@klmsk.dev"},"_npmVersion":"11.15.0","description":"Expo client for communicating with Bilt payments service. Handles the full iOS in-app purchase lifecycle (bootstrap, purchase, restore, entitlements, offline retry) against a tenant-based billing API.","directories":{},"sideEffects":false,"_nodeVersion":"26.0.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"knip":"^5.88.1","tsup":"^8.5.0","oxfmt":"^0.44.0","react":"19.1.0","oxlint":"^1.59.0","expo-iap":"^3.1.0","typescript":"^5.9.2","@types/react":"^19.1.8","react-native":"0.81.0","oxlint-tsgolint":"^0.20.0","oxlint-config-universe":"^0.0.3","@react-native-async-storage/async-storage":"^2.1.0"},"peerDependencies":{"react":">=18.0.0","expo-iap":">=3.0.0","react-native":">=0.72.0","@react-native-async-storage/async-storage":">=2.0.0"},"_npmOperationalInternal":{"tmp":"tmp/iap_0.1.7_1780572142227_0.2418723408738881","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@biltme/iap","version":"0.2.0","description":"Expo client for communicating with Bilt payments service. Handles the full iOS in-app purchase lifecycle (bootstrap, purchase, restore, entitlements, offline retry) against a tenant-based billing API.","keywords":["billing","bilt","entitlements","expo","expo-iap","iap","in-app-purchase","payments","react-native","storekit","subscriptions"],"license":"MIT","repository":{"type":"git","url":"git+https://github.com/buildingapplications/bilt-billing.git","directory":"packages/iap"},"type":"module","sideEffects":false,"main":"dist/index.js","module":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./package.json":"./package.json"},"publishConfig":{"access":"public"},"scripts":{"lint":"bun run lint:ci && bun run knip","lint:ci":"oxlint -c oxlint.config.cjs --deny-warnings src/","knip":"knip","format":"oxfmt --write src/ package.json","format:check":"oxfmt --check src/ package.json","typecheck":"tsc --noEmit && tsc -p tsconfig.test.json","test":"bun test src/__tests__","clean":"rm -rf dist","build":"bun run clean && tsup","pack:check":"npm pack --dry-run","prepublishOnly":"bun run format:check && bun run lint && bun run typecheck && bun run test && bun run build"},"devDependencies":{"@react-native-async-storage/async-storage":"^2.1.0","@types/bun":"^1.3.14","@types/react":"^19.1.8","@types/react-test-renderer":"^19.1.0","expo-iap":"^4.3.1","knip":"^5.88.1","oxfmt":"^0.44.0","oxlint":"^1.59.0","oxlint-config-universe":"^0.0.3","oxlint-tsgolint":"^0.20.0","react":"19.1.0","react-native":"0.81.0","react-test-renderer":"19.1.0","tsup":"^8.5.0","typescript":"^5.9.2"},"peerDependencies":{"@react-native-async-storage/async-storage":">=2.0.0","expo-iap":"^4.0.0","react":">=18.0.0","react-native":">=0.72.0"},"gitHead":"21329a959d239001cc7674fc56cd9b12c955bee5","_id":"@biltme/iap@0.2.0","bugs":{"url":"https://github.com/buildingapplications/bilt-billing/issues"},"homepage":"https://github.com/buildingapplications/bilt-billing#readme","_nodeVersion":"24.13.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-kA3F04y8ITHW5eHXs71Vn98+6OYqhiWMwbY1Mohst0AFrtbVcR1Rrkh2X6/ioksSERP2jq+38YvckTPK/r/Q+A==","shasum":"afdaa155910534aee6311ac6374afb28634acf10","tarball":"https://registry.npmjs.org/@biltme/iap/-/iap-0.2.0.tgz","fileCount":4,"unpackedSize":74764,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIEVAe5PnfH8DkkqdmWsTwjrbxhIFXXVbjEIS5FcaUv81AiEArBPAb0r1jUMX95kAkB+2VTnRcuCpqWJtWDC7Q+4tX/c="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:ebc414a3-7f78-44b3-8486-b820a8064fa8"}},"directories":{},"maintainers":[{"name":"biltmeadmin","email":"npm@bilt.me"},{"name":"kevinklmsk","email":"kevin@klmsk.dev"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/iap_0.2.0_1786019434746_0.32445995269069106"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-20T20:34:48.319Z","modified":"2026-08-06T12:30:35.091Z","0.1.0":"2026-04-20T20:34:48.598Z","0.1.1":"2026-04-21T13:17:46.455Z","0.1.2":"2026-04-21T17:00:31.786Z","0.1.3":"2026-04-22T16:28:18.769Z","0.1.4":"2026-04-27T13:20:24.230Z","0.1.5":"2026-04-27T14:56:48.505Z","0.1.6":"2026-04-30T15:50:01.936Z","0.1.7":"2026-06-04T11:22:22.374Z","0.2.0":"2026-08-06T12:30:34.884Z"},"license":"MIT","keywords":["billing","bilt","entitlements","expo","expo-iap","iap","in-app-purchase","payments","react-native","storekit","subscriptions"],"description":"Expo client for communicating with Bilt payments service. Handles the full iOS in-app purchase lifecycle (bootstrap, purchase, restore, entitlements, offline retry) against a tenant-based billing API.","maintainers":[{"name":"biltmeadmin","email":"npm@bilt.me"},{"name":"kevinklmsk","email":"kevin@klmsk.dev"}],"readme":"# `@biltme/iap`\n\nExpo client for communicating with Bilt payments service.\n\n`@biltme/iap` gives a React app a single provider plus a couple of hooks that\ncover the full iOS in-app purchase lifecycle:\n\n- Bootstrap the current user with the backend (authenticated or anonymous).\n- Fetch App Store product metadata through `expo-iap` in production, or build\n  mock product cards from configured product IDs in development.\n- Drive native purchases and forward receipts to `POST /iap/v1/purchases/ingest`.\n- Expose a backend-authoritative `entitlements` map to the UI.\n- Restore purchases via `expo-iap` + `POST /iap/v1/restore`.\n- Open the native Manage Subscriptions sheet.\n- Transparently retry ingest calls that failed with transient errors, persisted\n  across app restarts.\n- Support purchase-before-signup flows via anonymous identities that are later\n  linked to the authenticated user.\n\nOnly iOS is wired up today. The provider is safe to mount on Android — it will\nbootstrap entitlements and skip native store calls. On web, purchases and\nrestore are unavailable but entitlement reads still work.\n\n## Contents\n\n- [Installation](#installation)\n- [Quick start](#quick-start)\n- [Auth modes](#auth-modes)\n- [Configuration (`BiltIapConfig`)](#configuration-biltiapconfig)\n- [`BiltIapProvider`](#biltiapprovider)\n- [Hooks](#hooks)\n  - [`useBiltIAP()`](#usebiltiap)\n  - [`useEntitlement(code)`](#useentitlementcode)\n- [Entitlements](#entitlements)\n- [Products](#products)\n- [Anonymous identities](#anonymous-identities)\n- [Error handling (`BiltIapError`)](#error-handling-biltiaperror)\n- [Offline retry queue](#offline-retry-queue)\n- [Lifecycle behavior](#lifecycle-behavior)\n- [Backend endpoints used](#backend-endpoints-used)\n- [Metro / workspace resolution](#metro--workspace-resolution)\n\n## Installation\n\n```sh\nnpm install @biltme/iap\n# or\nbun add @biltme/iap\n```\n\nInstall the native peer dependencies in your app:\n\n- `react` >= 18\n- `react-native` >= 0.72\n- `expo-iap` 4.x\n- `@react-native-async-storage/async-storage` >= 2\n\nThey are peers because both the host app and `@biltme/iap` must see the exact\nsame copy. Duplicating `react` or `react-native` produces runtime errors like\n`Invalid hook call` or `Cannot read property 'useRef' of null`.\n\n> `expo-iap` requires a custom dev build. Expo Go cannot load it.\n\n## Quick start\n\n```tsx\nimport React from 'react';\nimport { BiltIapProvider, useBiltIAP, useEntitlement } from '@biltme/iap';\nimport type { BiltIapConfig } from '@biltme/iap';\n\nconst config: BiltIapConfig = {\n  tenantAppId: '11111111-1111-1111-1111-111111111111',\n  getAccessToken: async () => auth.getToken(),\n  productIds: [{ id: 'com.example.pro.monthly' }],\n  onError: (err) =>\n    console.warn('[BiltIAP]', {\n      code: err.code,\n      message: err.message,\n      retryable: err.retryable,\n      requestId: err.requestId,\n      cause: err.cause,\n    }),\n};\n\nexport default function App() {\n  return (\n    <BiltIapProvider config={config}>\n      <PaywallScreen />\n    </BiltIapProvider>\n  );\n}\n\nfunction PaywallScreen() {\n  const { initialized, products, purchaseProduct, restorePurchases } = useBiltIAP();\n  const pro = useEntitlement('pro');\n\n  if (!initialized) return null;\n  if (pro.active) return <ProFeatures />;\n\n  return (\n    <>\n      {products.map((p) => (\n        <Button\n          key={p.id}\n          title={`Buy ${p.title} — ${p.displayPrice}`}\n          onPress={() => purchaseProduct(p.id)}\n        />\n      ))}\n      <Button title=\"Restore Purchases\" onPress={() => restorePurchases()} />\n    </>\n  );\n}\n```\n\n> The `config` object is read once on mount. If it needs to change across\n> renders, memoize it with `useMemo` to avoid accidentally re-reading on\n> every render. The `getAccessToken` callback is re-invoked on every\n> request, so token rotation works without re-creating the provider.\n\n## Auth modes\n\nEach tenant app on the `bilt-billing` backend is configured for either bearer\nauth (Supabase or Bilt) or no auth. The SDK config you pass **must match the\ntenant's configuration**, otherwise every authenticated request fails with\n`unauthorized`.\n\n### 1. Bearer-auth tenant (`auth_provider = supabase` or `bilt`)\n\nThe backend validates the incoming access token against the tenant's\nconfigured `jwt_issuer`. The SDK is provider-agnostic: `getAccessToken` is an\nopaque token source, so the same wiring works whether the tenant uses Supabase\nor Bilt auth — only where you read the session token differs. You **must**\nprovide `getAccessToken` and return the current session's access token.\n\nSupabase:\n\n```tsx\nimport { supabase } from './supabaseClient';\n\nconst config: BiltIapConfig = {\n  tenantAppId: '...',\n  getAccessToken: async () => {\n    const { data } = await supabase.auth.getSession();\n    return data.session?.access_token ?? null;\n  },\n  productIds: [{ id: 'com.example.pro.monthly' }],\n};\n```\n\nBilt (`@bilt/backend`):\n\n```tsx\nimport { bilt } from './biltClient';\n\nconst config: BiltIapConfig = {\n  tenantAppId: '...',\n  getAccessToken: async () => {\n    const { data } = await bilt.auth.getSession();\n    return data.session?.access_token ?? null;\n  },\n  productIds: [{ id: 'com.example.pro.monthly' }],\n};\n```\n\nBehavior:\n\n- When the user is signed in, the SDK sends `Authorization: Bearer <jwt>`.\n  The backend derives the stable app user id from the JWT `sub` claim.\n- When the user is signed out, `getAccessToken` should return `null` /\n  `undefined`. The SDK falls back to an anonymous billing identity\n  (enabled by default) so the user can still purchase before signing in.\n- After the user signs in, call `linkAnonymousPurchasesToCurrentUser()` to\n  merge anonymous purchases into the authenticated account. See\n  [Anonymous identities](#anonymous-identities).\n\n### 2. No-auth tenant (`auth_provider = null`)\n\nThe backend does not accept bearer tokens for this tenant. **Omit\n`getAccessToken` entirely.** The SDK uses anonymous billing identities only:\n\n```tsx\nconst config: BiltIapConfig = {\n  tenantAppId: '...',\n  productIds: [{ id: 'com.example.pro.monthly' }],\n};\n```\n\nBehavior:\n\n- The SDK generates a stable `anon:<uuid>`, persists it in `AsyncStorage`,\n  and sends it as `X-Bilt-Anonymous-App-User-Id` on every request.\n- All entitlements are scoped to that anonymous id. The same device keeps\n  the same id across launches and purchases.\n- `linkAnonymousPurchasesToCurrentUser()` is not applicable.\n\n### Common mistakes\n\n- **Providing `getAccessToken` on a no-auth tenant**: the token is sent but\n  the backend rejects it (wrong provider). All requests fail.\n- **Omitting `getAccessToken` on a bearer-auth tenant**: the SDK runs in\n  anonymous-only mode, which is fine for signed-out users but means signed-in\n  users never become authenticated billing principals — they keep buying as\n  anonymous identities forever.\n- **Sending a custom user id header**: the SDK deliberately does not\n  accept or forward any client-controlled user id. Identity is always\n  derived by the backend from the verified bearer or the anonymous header.\n\n## Configuration (`BiltIapConfig`)\n\n| Field                | Type                                           | Required | Description                                                                                                          |\n| -------------------- | ---------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |\n| `backendUrl`         | `string`                                       | no       | Base URL of the `bilt-billing` backend. Defaults to `https://api.bilt.me`. Trailing slashes are stripped.        |\n| `headers`            | `Record<string, string>`                       | no       | Extra headers to include on every backend request. Useful for local tunnels such as ngrok.                          |\n| `tenantAppId`        | `string`                                       | yes      | Sent as `X-Bilt-Tenant-App-Id` on every request. Scopes the call to the correct tenant app. Sent trimmed, so surrounding whitespace is ignored. Not reactive: it is captured when the provider mounts, along with the rest of the tenant-scoped state (product catalog, anonymous identity, retry queue). To switch tenants, remount the provider with a new `key`. Must be non-empty: an empty or whitespace-only value (a common symptom of an unset `EXPO_PUBLIC_*` env var) fails every request with `invalid_request` instead of letting the backend answer `401 missing tenant app id header`. |\n| `getAccessToken`     | `() => Promise<string \\| null \\| undefined>`   | see [Auth modes](#auth-modes) | Required for bearer-auth tenants (Supabase or Bilt); return the current session's access token. Omit entirely for no-auth tenants. Return `null`/`undefined` when the user is signed out to fall back to anonymous billing. |\n| `anonymousIdentity`  | `{ enabled?: boolean; storageKey?: string }`   | no       | Enabled by default. Generates a stable anonymous billing principal for purchase-before-signup flows.                 |\n| `billingEnvironment` | `'mock' \\| 'production'`                       | no       | Explicit runtime environment. If omitted, `__DEV__ === true` uses `mock`; otherwise `production`.                    |\n| `productIds`         | `ConfiguredProduct[]`                          | yes      | Product config to fetch from the store. In mock mode, entries can also provide explicit `type`, `title`, `description`, `displayPrice`, `price`, and `currency`. |\n| `onError`            | `(err: BiltIapError) => void`                  | no       | Called whenever the provider surfaces an error. Useful for Sentry / Datadog / structured logging.                    |\n\nPointing at a local or staging billing backend:\n\n```tsx\nconst config: BiltIapConfig = {\n  backendUrl: 'http://127.0.0.1:8099',\n  tenantAppId: '11111111-1111-1111-1111-111111111111',\n  getAccessToken: async () => auth.getToken(),\n  productIds: [{ id: 'com.example.pro.monthly' }],\n};\n```\n\nUsing explicit product metadata for mock mode:\n\n```tsx\nconst config: BiltIapConfig = {\n  tenantAppId: '11111111-1111-1111-1111-111111111111',\n  billingEnvironment: 'mock',\n  productIds: [\n    {\n      id: 'com.buildingpp.subtrackr.aaa',\n      type: 'subs',\n      title: 'AAA',\n      description: 'Mock subscription product',\n    },\n    { id: 'com.buildingpp.subtrackr.pro_monthly_2' },\n  ],\n};\n```\n\nMock type inference now defaults unknown SKUs to `'subs'` and only treats clear\none-time hints (for example `lifetime`, `one-time`, or `inapp`) as `'in-app'`.\nYou can still set `type` explicitly to avoid any ambiguity. In production,\n`expo-iap` still provides the authoritative store metadata.\n\n## `BiltIapProvider`\n\n```tsx\n<BiltIapProvider config={config}>{children}</BiltIapProvider>\n```\n\nOn mount the provider:\n\n1. Resolves the billing principal: authenticated if `getAccessToken` returns\n   a token, otherwise anonymous (if enabled).\n2. Calls `GET /iap/v1/bootstrap` to obtain the `appAccountToken` and the\n   initial `entitlements` map.\n3. In mock mode, builds product cards from `productIds` and skips native\n   StoreKit. When a `productIds` entry is an object, its explicit mock metadata\n   is used instead of guessing from the SKU text.\n4. In production on iOS, probes native store availability via\n   `ExpoIap.initConnection()`. If the probe fails (web, Expo Go, missing\n   native module) the provider still loads entitlements but disables\n   purchase / restore.\n5. When the store is available, calls `ExpoIap.fetchProducts({ skus, type: 'all' })`\n   so mixed subscription + in-app catalogs load.\n6. Loads the persisted retry queue from `AsyncStorage` and flushes it.\n7. Subscribes to `ExpoIap.purchaseUpdatedListener` and\n   `ExpoIap.purchaseErrorListener`.\n8. Subscribes to `AppState` `\"change\"`. On foreground it re-fetches\n   entitlements and flushes the retry queue.\n\nOn unmount it removes listeners, clears any pending flush timer, and calls\n`ExpoIap.endConnection()` if a native connection was opened.\n\n## Hooks\n\n### `useBiltIAP()`\n\nReturns `BiltIapState & BiltIapApi`. Must be called inside a `BiltIapProvider`,\notherwise it throws `useBiltIAP must be used inside <BiltIapProvider>`.\n\nState:\n\n| Field                    | Type                                  | Notes                                                                             |\n| ------------------------ | ------------------------------------- | --------------------------------------------------------------------------------- |\n| `initialized`            | `boolean`                             | `true` once bootstrap + store init have resolved.                                 |\n| `loading`                | `boolean`                             | `true` until the initial bootstrap resolves (success or failure).                 |\n| `principalType`          | `'authenticated' \\| 'anonymous'`      | Current billing principal.                                                        |\n| `currentAppUserId`       | `string \\| undefined`                 | Stable backend user id for the active principal.                                  |\n| `billingEnvironment`     | `'mock' \\| 'production'`              | Runtime environment sent as `X-Bilt-Billing-Environment`.                         |\n| `purchasing`             | `boolean`                             | `true` while a purchase flow is in progress.                                      |\n| `restoring`              | `boolean`                             | `true` while `restorePurchases` is running.                                       |\n| `entitlements`           | `EntitlementMap`                      | Backend-authoritative map keyed by entitlement code (e.g. `\"pro\"`).               |\n| `appAccountToken`        | `string \\| undefined`                 | Stable per-user token the backend tags transactions with. Set after bootstrap.    |\n| `products`               | `StoreProduct[]`                      | App Store products for `config.productIds`. Empty on Android or if fetch failed.  |\n| `storeAvailable`         | `boolean`                             | `true` when native StoreKit/Billing is usable. `false` on web, Expo Go, failures. |\n| `storeUnavailableReason` | `string \\| undefined`                 | Human-readable reason the store is unavailable, if known.                         |\n| `lastError`              | `BiltIapError \\| undefined`           | Last error the provider surfaced.                                                 |\n| `pendingRetries`         | `number`                              | Number of ingest payloads sitting in the offline retry queue.                     |\n\nAPI methods:\n\n| Method                                                 | Description                                                                                                                                                                                                                     |\n| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `purchaseProduct(productId: string): Promise<void>`    | Production: kicks off the native purchase sheet. Mock: sends synthetic proof through the normal ingest route. Throws if the provider has not initialized, the store is unavailable, or the product is not in `products`.        |\n| `restorePurchases(): Promise<void>`                    | Production: calls `ExpoIap.restorePurchases()`, re-ingests every active purchase it finds, then calls `POST /iap/v1/restore`. Mock: calls backend restore directly. Updates `entitlements`.                                     |\n| `linkAnonymousPurchasesToCurrentUser(): Promise<void>` | Merge the current anonymous purchase history into the signed-in user. See [Anonymous identities](#anonymous-identities). Throws if not anonymous or no access token is available.                                               |\n| `refreshEntitlements(): Promise<void>`                 | Calls `GET /iap/v1/entitlements` and updates state. Never throws — errors are piped to `onError`.                                                                                                                               |\n| `hasEntitlement(code: string): boolean`                | Shortcut for `entitlements[code]?.active === true`.                                                                                                                                                                             |\n| `openManageSubscriptions(): Promise<void>`             | iOS: opens the native Manage Subscriptions sheet via `ExpoIap.deepLinkToSubscriptions`. Android: no-op without extra args.                                                                                                      |\n| `flushRetryQueue(): Promise<void>`                     | Force-flush the retry queue now (e.g. when the app regains connectivity).                                                                                                                                                       |\n\n### `useEntitlement(code)`\n\nConvenience hook for simple gate UIs.\n\n```tsx\nconst pro = useEntitlement('pro');\n\nif (pro.loading) return <Spinner />;\nif (pro.active) return <ProFeatures />;\nreturn <Paywall />;\n```\n\nReturns:\n\n| Field         | Type                                 |\n| ------------- | ------------------------------------ |\n| `active`      | `boolean`                            |\n| `status`      | `EntitlementStatus \\| undefined`     |\n| `entitlement` | `BiltEntitlement \\| undefined`       |\n| `loading`     | `boolean` (true until `initialized`) |\n\n## Entitlements\n\n```ts\ntype EntitlementStatus =\n  | 'active'\n  | 'grace_period'\n  | 'billing_retry'\n  | 'expired'\n  | 'revoked'\n  | 'refunded'\n  | 'purchased';\n\ntype BiltEntitlement = {\n  active: boolean;\n  status: EntitlementStatus;\n  platform: 'IOS' | 'Android';\n  productId?: string;\n  currentPlanId?: string;\n  expirationDate?: number;   // epoch ms\n  isAutoRenewing?: boolean;\n  gracePeriod?: boolean;\n  billingRetry?: boolean;\n  updatedAt?: number;        // epoch ms\n};\n\ntype EntitlementMap = Record<string, BiltEntitlement>;\n```\n\nEntitlements are always authored by the backend. The client never derives\nentitlement state from a local purchase — it only forwards receipts and\nre-reads the map.\n\n## Products\n\n```ts\ntype StoreProduct = {\n  id: string;\n  title: string;\n  description: string;\n  displayPrice: string;      // localized, e.g. \"$4.99\"\n  price: number;             // numeric price in `currency`\n  currency: string;\n  store: 'apple' | 'google' | 'unknown';\n  type: 'subs' | 'in-app';\n};\n```\n\n`type` is inferred from `expo-iap`'s `typeIOS`. `auto-renewable-subscription`\nand `non-renewing-subscription` are treated as `'subs'`, everything\nelse as `'in-app'`. `purchaseProduct` uses this to pick between `type: 'subs'`\nand `type: 'in-app'` when calling `ExpoIap.requestPurchase`.\n\nIn mock mode, `type` can be provided explicitly in `config.productIds` object\nentries. If omitted, the SDK falls back to the legacy SKU-name heuristic.\n\n## Anonymous identities\n\nThe provider supports purchase-before-signup. When `getAccessToken` returns\n`null`/`undefined` and `anonymousIdentity.enabled` is not `false` (the\ndefault), a stable anonymous billing principal is generated, stored in\n`AsyncStorage`, and sent to the backend as `X-Bilt-Anonymous-App-User-Id`.\nThe user can buy and hold entitlements while signed out.\n\nAfter the user signs in, call `linkAnonymousPurchasesToCurrentUser()` to\nmerge the anonymous history into the authenticated account:\n\n```tsx\nconst { linkAnonymousPurchasesToCurrentUser } = useBiltIAP();\n\nasync function onSignInComplete() {\n  try {\n    await linkAnonymousPurchasesToCurrentUser();\n  } catch (err) {\n    // Already authenticated, no anonymous id, or missing access token\n  }\n}\n```\n\nLinking is also attempted automatically at the start of `purchaseProduct` and\n`restorePurchases` when an access token becomes available, so in most apps\nyou do not need to call it explicitly.\n\n`purchaseProduct` and `restorePurchases` also re-resolve the billing principal\nwhen `getAccessToken` starts returning a different token (a sign-out, or a\nswitch to another user). Without that, the app account token sent to StoreKit\nwould still belong to the previous user.\n\nCustomize the storage key if you need to isolate multiple apps in the same\nbundle:\n\n```tsx\nconst config: BiltIapConfig = {\n  // ...\n  anonymousIdentity: { storageKey: '@myapp:bilt-anon-id' },\n};\n```\n\n## Error handling (`BiltIapError`)\n\nEvery error surfaced by the provider is a `BiltIapError`.\n\n```ts\nclass BiltIapError extends Error {\n  readonly code: BiltIapErrorCode;\n  readonly retryable: boolean;\n  readonly requestId?: string;\n}\n```\n\nCodes:\n\n| Code                     | Source  | Meaning                                                |\n| ------------------------ | ------- | ------------------------------------------------------ |\n| `unauthorized`           | backend | Caller is not allowed.                                 |\n| `invalid_request`        | backend | Validation failed.                                     |\n| `ownership_mismatch`     | backend | Transaction belongs to another `appAccountToken`.      |\n| `product_not_configured` | backend | Product id is not mapped in the tenant catalog.        |\n| `store_unavailable`      | backend | Upstream App Store call failed.                        |\n| `internal_error`         | backend | Fallback for unexpected backend failures.              |\n| `billing_user_not_found` | backend | The backend has no record of this user.                |\n| `notification_invalid`   | backend | Apple notification was rejected.                       |\n| `not_initialized`        | client  | Provider has not finished init / token missing.        |\n| `purchase_cancelled`     | client  | User dismissed the purchase sheet.                     |\n| `purchase_pending`       | client  | Ask-to-buy / deferred purchase.                        |\n| `purchase_failed`        | client  | Native purchase flow failed.                           |\n| `network_error`          | client  | `fetch` rejected. Always `retryable: true`.            |\n| `store_not_available`    | client  | Store connection never came up.                        |\n| `product_not_found`      | client  | `purchaseProduct` called with an id not in `products`. |\n\nOnly `refreshEntitlements` swallows its own errors (into `lastError` /\n`onError`). `purchaseProduct`, `restorePurchases`, and\n`linkAnonymousPurchasesToCurrentUser` route errors to `onError` and also\nrethrow so callers can show per-action UI.\n\n## Offline retry queue\n\nIngest calls (`POST /iap/v1/purchases/ingest`) are critical — they turn a\nreal StoreKit receipt into an entitlement. If one fails with a\n`retryable: true` error, the payload is enqueued in the retry queue and\npersisted to `AsyncStorage` under the key `@biltme/iap:retry-queue`.\n\nRetry behavior:\n\n- Exponential backoff: `min(2s * 2^attempts, 5min)` with 50–100% jitter.\n- Up to `8` attempts per payload. After that the item is dropped (dead\n  letter) rather than poisoning the queue.\n- Flushed on: enqueue, app foreground, and when `flushRetryQueue()` is\n  called explicitly.\n- Non-retryable errors (`invalid_request`, `ownership_mismatch`, etc.) drop\n  the item immediately instead of retrying.\n\n`pendingRetries` is exposed so the UI can surface a banner:\n\n```tsx\n{pendingRetries > 0 && (\n  <Text>{pendingRetries} pending retries in queue</Text>\n)}\n```\n\n## Lifecycle behavior\n\n- **`purchase-updated` listener**: ingests the receipt, then calls\n  `ExpoIap.finishTransaction({ purchase })` only if the backend returns\n  `finishTransaction: true`. Pending (`ask-to-buy`) purchases are ignored\n  until they resolve — the user does not get access yet.\n- **`purchase-error` listener**: `user-cancelled` is silently dropped; all\n  other codes surface via `onError`.\n- **Foreground refresh**: when `AppState` flips to `\"active\"` and the\n  provider has initialized, it re-fetches entitlements and flushes the\n  retry queue. This is how server-side lifecycle events (renew, expire,\n  refund) reach the client without an explicit pull.\n- **Restore**: calls `ExpoIap.restorePurchases()` first (so the native\n  receipt refresh happens), then `ExpoIap.getAvailablePurchases` with\n  `onlyIncludeActiveItemsIOS: true`, re-ingests each unique transaction,\n  then hits `POST /iap/v1/restore` to let the backend reconcile.\n- **Mock runtime**: skips native store calls, builds mock product cards\n  from `productIds`, and sends synthetic purchase proof through\n  `POST /iap/v1/purchases/ingest`. Entitlements still come only from the\n  backend. Mock product metadata can be supplied explicitly so the UI does not\n  have to infer subscription-vs-in-app from the product id string.\n\n## Backend endpoints used\n\nAll requests are JSON and carry these headers:\n\n- `Content-Type: application/json`\n- `X-Bilt-Tenant-App-Id: <config.tenantAppId.trim()>`\n- `X-Bilt-Billing-Environment: mock | production`\n- `Authorization: Bearer <await config.getAccessToken()>` when authenticated,\n  or `X-Bilt-Anonymous-App-User-Id: <id>` when anonymous\n\nThe backend uses the bearer token or anonymous header to derive the stable\nbilling principal. `tenantAppId` is tenant routing only. Missing billing\nenvironment headers default to `production`, but the SDK always sends one.\n\n| Method | Path                       | When                                                  |\n| ------ | -------------------------- | ----------------------------------------------------- |\n| GET    | `/iap/v1/bootstrap`        | Mount.                                                |\n| GET    | `/iap/v1/entitlements`     | Foreground, `refreshEntitlements()`.                  |\n| POST   | `/iap/v1/purchases/ingest` | After every `purchase-updated` and during restore.    |\n| POST   | `/iap/v1/restore`          | `restorePurchases()`.                                 |\n| POST   | `/iap/v1/link`             | `linkAnonymousPurchasesToCurrentUser()`.              |\n\nResponses follow the shape `{ ok: true, data: T }` on success and\n`{ ok: false, error: { code, message, retryable?, requestId? } }` on\nfailure. The client reads `retryable` from the body when present and falls\nback to \"HTTP 5xx => retryable\" otherwise.\n\n## Metro / workspace resolution\n\nWhen consuming `@biltme/iap` from a local workspace path inside an Expo app,\nMetro must resolve `react`, `react-native`, and the native peers from the\n**host app's** `node_modules`, not from `packages/iap/node_modules`.\nOtherwise you will see:\n\n- `Invalid hook call`\n- `Cannot read property 'useRef' of null`\n\nThe fix is to add the package's path to Metro's `watchFolders` and set\n`resolver.nodeModulesPaths` to the host app's `node_modules` only.\n","readmeFilename":"README.md","homepage":"https://github.com/buildingapplications/bilt-billing#readme","repository":{"type":"git","url":"git+https://github.com/buildingapplications/bilt-billing.git","directory":"packages/iap"},"bugs":{"url":"https://github.com/buildingapplications/bilt-billing/issues"}}