React-native-iap: IOS๋Š” ์ž๋™ ๊ฐฑ์‹  ๊ตฌ๋…์ด ์•„์ง ํ™œ์„ฑ ์ƒํƒœ์ธ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

์— ๋งŒ๋“  2018๋…„ 09์›” 27์ผ  ยท  61์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: dooboolab/react-native-iap

react-native-iap ๋ฒ„์ „

2.2.2

์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ํ”Œ๋žซํผ(IOS ๋˜๋Š” Android ๋˜๋Š” ๋‘˜ ๋‹ค?)

IOS

์˜ˆ์ƒ๋˜๋Š” ํ–‰๋™

RNIAP API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž๋™ ๊ฐฑ์‹  ๊ตฌ๋…์ด ์—ฌ์ „ํžˆ ํ™œ์„ฑ ์ƒํƒœ์ธ์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‹ค์ œ ํ–‰๋™

Android์—์„œ ๋‹ค์Œ์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

export const isUserSubscriptionActive = async (subscriptionId) =>{
    // Get all the items that the user has
    const availablePurchases = await getAvailablePurchases();
    if(availablePurchases !== null && availablePurchases.length > 0){
        const subscription = availablePurchases.find((element)=>{
            return subscriptionId === element.productId;
        });
        if(subscription){
             // check for the autoRenewingAndroid flag. If it is false the sub period is over
              return subscription["autoRenewingAndroid"] == true;
            }
        }else{
            return false;
        }
    }
}

ios์—๋Š” ์ด๋ฅผ ํ™•์ธํ•˜๋Š” ํ”Œ๋ž˜๊ทธ๊ฐ€ ์—†์œผ๋ฉฐ getAvailablePurchases ๋ฉ”์„œ๋“œ๋Š” ํ˜„์žฌ ํ™œ์„ฑํ™”๋˜์ง€ ์•Š์€ ๊ตฌ๋…์„ ํฌํ•จํ•˜์—ฌ ์ด๋ฃจ์–ด์ง„ ๋ชจ๋“  ๊ตฌ๋งค๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๊นŒ?

๋ฌธ์•ˆ ์ธ์‚ฌ,
๋งˆ๋ฅด์ฝ”์Šค

๐Ÿ“ฑ iOS ๐Ÿšถ๐Ÿป stale

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

๋‹ค์Œ์€ Android์™€ iOS ๋ชจ๋‘์—์„œ ์ž‘๋™ํ•˜๋ฉฐ ์ตœ์‹  ์˜์ˆ˜์ฆ ๋ฐ์ดํ„ฐ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋„๋ก iOS์—์„œ ์ ์ ˆํ•˜๊ฒŒ ์ •๋ ฌํ•˜๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

import * as RNIap from 'react-native-iap';
import {ITUNES_CONNECT_SHARED_SECRET} from 'react-native-dotenv';

const SUBSCRIPTIONS = {
  // This is an example, we actually have this forked by iOS / Android environments
  ALL: ['monthlySubscriptionId', 'yearlySubscriptionId'],
}

async function isSubscriptionActive() {
  if (Platform.OS === 'ios') {
    const availablePurchases = await RNIap.getAvailablePurchases();
    const sortedAvailablePurchases = availablePurchases.sort(
      (a, b) => b.transactionDate - a.transactionDate
    );
    const latestAvailableReceipt = sortedAvailablePurchases[0].transactionReceipt;

    const isTestEnvironment = __DEV__;
    const decodedReceipt = await RNIap.validateReceiptIos(
      {
        'receipt-data': latestAvailableReceipt,
        password: ITUNES_CONNECT_SHARED_SECRET,
      },
      isTestEnvironment
    );
    const {latest_receipt_info: latestReceiptInfo} = decodedReceipt;
    const isSubValid = !!latestReceiptInfo.find(receipt => {
      const expirationInMilliseconds = Number(receipt.expires_date_ms);
      const nowInMilliseconds = Date.now();
      return expirationInMilliseconds > nowInMilliseconds;
    });
    return isSubValid;
  }

  if (Platform.OS === 'android') {
    // When an active subscription expires, it does not show up in
    // available purchases anymore, therefore we can use the length
    // of the availablePurchases array to determine whether or not
    // they have an active subscription.
    const availablePurchases = await RNIap.getAvailablePurchases();

    for (let i = 0; i < availablePurchases.length; i++) {
      if (SUBSCRIPTIONS.ALL.includes(availablePurchases[i].productId)) {
        return true;
      }
    }
    return false;
  }
} 

๋ชจ๋“  61 ๋Œ“๊ธ€

๋‚˜๋„ ์ด๊ฒƒ์„ ์•Œ์•„๋‚ด๊ธฐ ์œ„ํ•ด ๋…ธ๋ ฅํ•˜๊ณ  ์žˆ๋‹ค. ์•„์ง ํ…Œ์ŠคํŠธํ•˜์ง€ ์•Š์•˜์ง€๋งŒ ๋‚ด๊ฐ€ ์ฝ์€ ๋‚ด์šฉ์—์„œ iTunes Connect์—์„œ "๊ณต์œ  ๋น„๋ฐ€"์„ ๋งŒ๋“ค๊ณ  '๋น„๋ฐ€๋ฒˆํ˜ธ' ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฅผ validateReceiptIos์— ์ „๋‹ฌํ•˜๋ฉด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์ˆ˜์ง‘ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๊ตฌ๋…์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์ƒํƒœ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์ฝ”๋“œ์™€ ํŠนํžˆ ๊ตฌ๋… ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” latest_receipt, latest_receipt_info ๋ฐ latest_expired_receipt ์ •๋ณด๊ฐ€ ํฌํ•จ๋œ JSON ๊ฐœ์ฒด๊ฐ€ ๋ฐ˜ํ™˜๋  ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋ฌธ์ž ๊ทธ๋Œ€๋กœ ์ด๊ฒƒ์„ ์•Œ์•„ ๋‚ด๊ณ  ์žˆ์œผ๋ฏ€๋กœ ์•„์ง ํ…Œ์ŠคํŠธํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋ฌธ์ œ์™€ Apple์˜ ๋ฌธ์„œ๋ฅผ ๋ชจ์•„ ๋†“์€ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ํšจ๊ณผ๊ฐ€ ์žˆ๋‹ค๋ฉด ๋ฌธ์ œ์— ๋ฌปํžˆ์ง€ ์•Š๊ณ  ๋ฌธ์„œ์—์„œ ๋งค์šฐ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ฐํ˜€์•ผ ํ•ฉ๋‹ˆ๋‹ค.
๋‹ค์Œ ๋งํฌ๊ฐ€ ๊ด€๋ จ์ด ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.
https://developer.apple.com/library/archive/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html

203 #237

ํŽธ์ง‘ : ์œ„์—์„œ ์–ธ๊ธ‰ํ•œ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ์šฐ๋ฆฌ๊ฐ€ ๋ฌธ์„œ์—์„œ ์ด๊ฒƒ์— ๋Œ€ํ•œ ์™„์ „ํ•œ ์„ค๋ช…์„ ์–ป๊ธฐ ์œ„ํ•ด ๋…ธ๋ ฅํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๊ตฌ๋…์ด ๋งŒ๋ฃŒ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์•ฑ ์‹œ์ž‘ ์‹œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

RNIap.getPurchaseHistory()
        .then(purchases => {
                RNIap.validateReceiptIos({
                   //Get receipt for the latest purchase
                    'receipt-data': purchases[purchases.length - 1].transactionReceipt,
                    'password': 'whateveryourpasswordis'
                }, __DEV__)
                    .then(receipt => {
                       //latest_receipt_info returns an array of objects each representing a renewal of the most 
                       //recently purchased item. Kinda confusing terminology
                        const renewalHistory = receipt.latest_receipt_info
                       //This returns the expiration date of the latest renewal of the latest purchase
                        const expiration = renewalHistory[renewalHistory.length - 1].expires_date_ms
                       //Boolean for whether it has expired. Can use in your app to enable/disable subscription
                        console.log(expiration > Date.now())
                    })
                    .catch(error => console.log(`Error`))
        })

@kevinEsherick

ํ›Œ๋ฅญํ•œ!

๊ท€ํ•˜์˜ ์ฝ”๋“œ์— ๋Œ€ํ•ด ๊ทธ๋‹ค์ง€ ์‚ฌ์†Œํ•˜์ง€ ์•Š์€ ๋ช‡ ๊ฐ€์ง€ ์‚ฌํ•ญ;

  1. ๊ทธ๊ฒƒ์€ํ•ด์•ผํ•œ๋‹ค

const ๋งŒ๋ฃŒ = ๊ฐฑ์‹  ๊ธฐ๋ก [renewalHistory.length - 1].expires_date_ms

๋Œ€์‹  (์•„๋งˆ๋„ ๊ทธ๋ƒฅ ์˜คํƒ€)

const ๋งŒ๋ฃŒ = latestRenewalReceipt [latestRenewal.length - 1].expires_date_ms

2) '๋น„๋ฐ€๋ฒˆํ˜ธ'๊ฐ€ ๋ฌด์—‡์ธ์ง€ ๋ชจ๋ฅด๋Š” ๋ถ„๋“ค์„ ์œ„ํ•ด: '๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ๋ฌด์—‡์ด๋“ ':

์ธ์•ฑ ํŽ˜์ด์ง€์— ๋Œ€ํ•œ ๊ณต์œ  ๋น„๋ฐ€ ํ‚ค๋ฅผ ๋งŒ๋“ค๊ณ  ์•ฑ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹จ๊ณ„๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. https://www.appypie.com/faqs/how-can-i-get-shared-secret-key-for-in-app-purchase

+1 ๋ฌธ์„œ ์—…๋ฐ์ดํŠธ, ๋˜ํ•œ @dooboolab IOS์™€ Android์—์„œ ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋ช‡ ๊ฐ€์ง€ ๋‹ค๋ฅธ ์ ์ด ์žˆ๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์›ํ•˜์‹œ๋ฉด ๋„์™€๋“œ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฌธ์•ˆ ์ธ์‚ฌ

@kevinEsherick

์ž๋™ ๊ฐฑ์‹  ๊ตฌ๋…์€ ์–ด๋–ป๊ฒŒ ๋ฉ๋‹ˆ๊นŒ?

expires_date_ms๊ฐ€ ์ฒซ ๋ฒˆ์งธ ๋งŒ๋ฃŒ ๋‚ ์งœ์ธ ๊ฒƒ ๊ฐ™์œผ๋ฉฐ ์—…๋ฐ์ดํŠธ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด๊ฒƒ์„ ํ…Œ์ŠคํŠธํ–ˆ๋‹ค :

  1. ๊ตฌ๋… ํ•ญ๋ชฉ ๊ตฌ๋…
  2. ๋งŒ๋ฃŒ ๋‚ ์งœ ํ™•์ธ(์˜ˆ: 5๋ถ„ ํ›„)
  3. 10๋ถ„ ๊ธฐ๋‹ค๋ ค
  4. ๊ตฌ๋…์ด ์—ฌ์ „ํžˆ ํ™œ์„ฑ ์ƒํƒœ์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ž๋™ ๊ฐฑ์‹  ๊ตฌ๋…์ž…๋‹ˆ๋‹ค.
  5. ๋งˆ์ง€๋ง‰ ๊ฐฑ์‹  ์˜์ˆ˜์ฆ ๋งŒ๋ฃŒ๋ฅผ ํ™•์ธํ•˜๋ฉด ์—ฌ์ „ํžˆ ํฌ์ธํŠธ 2์™€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

์–ด๋–ค ์•„์ด๋””์–ด?

๋‚ด ๋‚˜์œ, ๋งŒ๋ฃŒ ๋‚ ์งœ๊ฐ€ ์—…๋ฐ์ดํŠธ ๋œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋‹จ์ง€ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฝ๋‹ˆ๋‹ค.

๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ๊ฐ™์€ ์‹œ๋‚˜๋ฆฌ์˜ค์— ์žˆ๋Š” ๊ฒฝ์šฐ๋ฅผ ๋Œ€๋น„ํ•˜์—ฌ ์—ฌ๊ธฐ์— ์ด โ€‹โ€‹์˜๊ฒฌ์„ ๋‚จ๊ธธ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ฌธ์•ˆ ์ธ์‚ฌ

  1. ๊ทธ๊ฒƒ์€ํ•ด์•ผํ•œ๋‹ค

const ๋งŒ๋ฃŒ = ๊ฐฑ์‹  ๊ธฐ๋ก [renewalHistory.length - 1].expires_date_ms

๋Œ€์‹  (์•„๋งˆ๋„ ๊ทธ๋ƒฅ ์˜คํƒ€)

const ๋งŒ๋ฃŒ = latestRenewalReceipt [latestRenewal.length - 1].expires_date_ms

์•„ ๋งž๋‹ค, ์˜คํƒ€๊ฐ€ ๋งž์•˜๋‹ค. ๊ทธ ์˜๋ฏธ๋ฅผ ๋” ๋ช…ํ™•ํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ์ด๋ฆ„์„ ๋ณ€๊ฒฝํ–ˆ์ง€๋งŒ ๋ชจ๋“  ํ•ญ๋ชฉ์„ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์„ ์žŠ์—ˆ์Šต๋‹ˆ๋‹ค. ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค, ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ๋งŒ๋ฃŒ ๋‚ ์งœ๊ฐ€ ์žˆ๋Š” ์ •ํ™•ํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค๋Š” ์—†์—ˆ์ง€๋งŒ ์ž๋™ ๊ฐฑ์‹ ์ด ์ „ํ˜€ ๋˜์ง€ ์•Š๋Š” ์ผ๋ถ€ ํ•ญ๋ชฉ์ด ์žˆ์—ˆ์ง€๋งŒ ์ž์ฒด์ ์œผ๋กœ ํ•ด๊ฒฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค(๊ฑฑ์ •๋˜์ง€๋งŒ ์žฌ์ƒ์‚ฐํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ๋‹ค๋ฅธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•จ). ๋‹น์‹ ์ด ๊ฐ€์ง„ ๋ฌธ์ œ๋Š” ์ƒ๋‹นํžˆ ์ผ๋ฐ˜์ ์ธ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. Apple ์ธก์—์„œ ์˜ˆ์ƒํ•œ ๋™์ž‘์ด๋ฏ€๋กœ ์‚ฌ์šฉ์ž๋ฅผ ๊ตฌ๋… ์ทจ์†Œํ•˜๊ธฐ ์ „์— ๊ตฌ๋…์ด ๊ฐฑ์‹ ๋  ์ˆ˜ ์žˆ๋„๋ก ๋ฒ„ํผ ๊ธฐ๊ฐ„์„ ๋‚จ๊ฒจ ๋‘์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. SO์— ๋Œ€ํ•œ ์ด ๋ฌธ์ œ๋Š” https://stackoverflow.com/questions/42158460/autorenewable-subscription-iap-renewing-after-expiry-date-in-sandbox ์—์„œ ์ž์„ธํžˆ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

๊ฐ์‚ฌ ํ•ด์š”! ์ข‹์€ ์ •๋ณด, ์ด์œ ๊ฐ€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

์ด ๋ฌธ์ œ์— ๋Œ€ํ•ด @kevinEsherick ์ฝ”๋“œ๊ฐ€ ๋ฌธ์„œ์— ์žˆ์–ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค

์˜ˆ. PR ์š”์ฒญํ•˜๋Š” ์‚ฌ๋žŒ์ด ์žˆ์œผ๋ฉด readme ์˜ ๊น”๋”ํ•œ ๋ฌธ์„œ๋ฅผ ๋ณด๋‚ด์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ฐ์‚ฌ ํ•ด์š”.

์•ˆ๋…• ์–˜๋“ค์•„. Readme์— Q & A ์„น์…˜ ์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ˆ„๊ตฌ๋“ ์ง€ ์ด ๋ฌธ์ œ์— ๋Œ€ํ•ด PR ๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.

๋ฉฐ์น  ๋‚ด๋กœ PR์„ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋…ธ๋ ฅํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. :)

๋‚˜๋Š” ๋˜ํ•œ ํŠนํžˆ ๊ตฌ๋…์„ ์œ„ํ•ด ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. iOS์™€ ์•ˆ๋“œ๋กœ์ด๋“œ ๋ชจ๋‘. ์‚ฌ์šฉ์ž์—๊ฒŒ ์œ ํšจํ•œ ๊ตฌ๋…์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ์ ์ ˆํ•œ ๋ฐฉ๋ฒ•์„ ๋ฌธ์„œํ™”ํ•˜๋ฉด ๋Œ€๋‹จํžˆ ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๐Ÿ˜„

@curiousdustin ์ด์— ๋Œ€ํ•œ Apple์˜ ๊ตฌํ˜„์€ medium ์„ ์ฐธ์กฐํ•˜๋ฉด ์ •๋ง ๋”์ฐํ•ฉ๋‹ˆ๋‹ค. ๋ฐฑ์—”๋“œ์—์„œ ์ด๋Ÿฌํ•œ ์ข…๋ฅ˜์˜ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๋ชจ๋“ˆ์—์„œ ์ด๋ฅผ ์™„์ „ํžˆ ์ง€์›ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ iOS ๊ตฌ๋… ์ œํ’ˆ์—์„œ๋Š” ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” getAvailablePurchases ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ Android์—์„œ availablePurchases ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ @marcosmartinez7 ๊ณผ @kevinEsherick ์ด ์ด ์Šค๋ ˆ๋“œ์—์„œ ์ž‘์—…ํ•˜๊ณ  ์žˆ๋Š” ์†”๋ฃจ์…˜์ด ์†”๋ฃจ์…˜์ด ์•„๋‹ˆ๋ฉฐ iOS ๊ตฌ๋… ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด Apple ์ด์™ธ์˜ ๋ฐฑ์—”๋“œ ์„œ๋ฒ„๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋Š” ์‹ ๊ฐ€์š” ?

Apple ๋ฌธ์„œ์™€ ํ•จ๊ป˜ ๊ฒŒ์‹œํ•œ Medium ๊ธฐ์‚ฌ๋ฅผ ๋ณด๋ฉด ์„œ๋ฒ„๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์„ ํ˜ธ๋˜๋Š” ๊ฒƒ ๊ฐ™์ง€๋งŒ ์žฅ์น˜๋งŒ์œผ๋กœ ๊ตฌ๋… ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ด ๋ถˆ๊ฐ€๋Šฅํ•œ ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค.

์ œ ๊ธ€์— ์ •๋ณด๊ฐ€ ๋ˆ„๋ฝ๋œ ์  ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ์ง€๊ธˆ ๊ด€๋ฆฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
Apple ๋Š” ์ค‘๊ฐ„ ๊ณต๊ฒฉ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด receipt ๋ฅผ ์ž์‹ ์˜ ์„œ๋ฒ„์— ์ €์žฅํ•˜๋Š” ๊ฒƒ์„ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋‚˜์ค‘์— ์˜์ˆ˜์ฆ์„ ํ™•์ธํ•˜์—ฌ ์˜์ˆ˜์ฆ์ด ์œ ํšจํ•œ์ง€ ๋˜๋Š” ๊ตฌ๋…์„ ๊ณ„์† ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ๊ฐ์‚ฌํ•˜๊ฒŒ๋„ @marcosmartinez7 ๊ณผ @kevinEsherick์ด ํ•ด๊ฒฐํ•œ ์ž์ฒด Apple ์„œ๋ฒ„(์ƒŒ๋“œ๋ฐ•์Šค ๋ฐ ํ”„๋กœ๋•์…˜)์— ๊ฒ€์ฆ ์˜์ˆ˜์ฆ์˜ ์ง์ ‘ ๊ฐ€์ ธ์˜ค๊ธฐ๋ฅผ ์ œ๊ณตํ•˜๊ธฐ ๋•Œ๋ฌธ์— react-native-iap ๋กœ ๊ณ„์† ์‹œ๋„ํ•˜๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํ˜„์žฌ ์ด๊ฒƒ์ด ios ๊ตฌ๋…์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์•ˆ๋…•ํ•˜์„ธ์š”, ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ๋ฉฐ์น  ์ด์ƒ ์ง€๋‚ฌ์Šต๋‹ˆ๋‹ค. ์ผ์— ๋ชฐ๋‘ํ–ˆ์ง€๋งŒ ์—ฌ์ „ํžˆ README๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ธฐ ์œ„ํ•ด ํ•ด๋‹น PR์„ ์ œ์ถœํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. @curiousdustin ์œ„์—์„œ ๋‚ด๊ฐ€ ์“ด ๋ฐฉ๋ฒ•์€ ํ™•์‹คํžˆ ํšจ๊ณผ๊ฐ€ ์žˆ์œผ๋ฉฐ ๋ฌธ์„œ์— ์ถ”๊ฐ€ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์˜ค๋Š˜ ๋ฐค์ด๋‚˜ ๋‚ด์ผ PR ํ• ๊ฒŒ์š” :)

์—…๋ฐ์ดํŠธ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

๋‹น์‹ ์˜ ์˜ˆ์—์„œ ํ•œ ๊ฐ€์ง€ ๋” ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค.

'์˜์ˆ˜์ฆ ๋ฐ์ดํ„ฐ': ๊ตฌ๋งค[purchases.length - 1].transactionReceipt,
...
const ๋งŒ๋ฃŒ = ๊ฐฑ์‹  ๊ธฐ๋ก[renewalHistory.length - 1].expires_date_ms

๋‚ด ํ…Œ์ŠคํŠธ์—์„œ ๊ตฌ๋งค ๋ฐ ๊ฐฑ์‹  ๊ธฐ๋ก์ด ๋ฐ˜๋“œ์‹œ ํŠน์ • ์ˆœ์„œ๋Š” ์•„๋‹™๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์˜๋„ํ•œ ๊ฒƒ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ๊ทธ๊ฒƒ๋“ค์„ ์ •๋ ฌํ•˜๊ฑฐ๋‚˜ ๋ญ”๊ฐ€๋ฅผ ํ•ด์•ผ ํ•˜์ง€ ์•Š์„๊นŒ์š”?

์—ฌ๊ธฐ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค. ๊ตฌ๋งค ๋ฐ ๊ฐฑ์‹  ๊ธฐ๋ก์€ ํ™•์‹คํžˆ ์ฃผ๋ฌธ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋จผ์ € ์ •๋ ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. dev์—์„œ๋Š” ๋งŽ์€ ๋ฐ˜๋ณต์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ์€ Android์™€ iOS ๋ชจ๋‘์—์„œ ์ž‘๋™ํ•˜๋ฉฐ ์ตœ์‹  ์˜์ˆ˜์ฆ ๋ฐ์ดํ„ฐ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋„๋ก iOS์—์„œ ์ ์ ˆํ•˜๊ฒŒ ์ •๋ ฌํ•˜๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

import * as RNIap from 'react-native-iap';
import {ITUNES_CONNECT_SHARED_SECRET} from 'react-native-dotenv';

const SUBSCRIPTIONS = {
  // This is an example, we actually have this forked by iOS / Android environments
  ALL: ['monthlySubscriptionId', 'yearlySubscriptionId'],
}

async function isSubscriptionActive() {
  if (Platform.OS === 'ios') {
    const availablePurchases = await RNIap.getAvailablePurchases();
    const sortedAvailablePurchases = availablePurchases.sort(
      (a, b) => b.transactionDate - a.transactionDate
    );
    const latestAvailableReceipt = sortedAvailablePurchases[0].transactionReceipt;

    const isTestEnvironment = __DEV__;
    const decodedReceipt = await RNIap.validateReceiptIos(
      {
        'receipt-data': latestAvailableReceipt,
        password: ITUNES_CONNECT_SHARED_SECRET,
      },
      isTestEnvironment
    );
    const {latest_receipt_info: latestReceiptInfo} = decodedReceipt;
    const isSubValid = !!latestReceiptInfo.find(receipt => {
      const expirationInMilliseconds = Number(receipt.expires_date_ms);
      const nowInMilliseconds = Date.now();
      return expirationInMilliseconds > nowInMilliseconds;
    });
    return isSubValid;
  }

  if (Platform.OS === 'android') {
    // When an active subscription expires, it does not show up in
    // available purchases anymore, therefore we can use the length
    // of the availablePurchases array to determine whether or not
    // they have an active subscription.
    const availablePurchases = await RNIap.getAvailablePurchases();

    for (let i = 0; i < availablePurchases.length; i++) {
      if (SUBSCRIPTIONS.ALL.includes(availablePurchases[i].productId)) {
        return true;
      }
    }
    return false;
  }
} 

@andrewze

Android ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ ๊ตฌ๋…์ด ์ž๋™ ๊ฐฑ์‹  ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ availablePurchases์— ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.

@curiousdustin @andrewzey ๋งž์Šต๋‹ˆ๋‹ค. ๊ตฌ๋งค๋Š” ์ฃผ๋ฌธ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, RenewalHistory/latest_receipt_info๋Š” ํ•ญ์ƒ ์ €๋ฅผ ์œ„ํ•ด ์ฃผ๋ฌธ๋ฉ๋‹ˆ๋‹ค. ๊ตฌ๋งค๊ฐ€ ์ฃผ๋ฌธ๋˜์ง€ ์•Š์•˜๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ฌ์•˜์ง€๋งŒ ์ด๊ฒƒ์ด ์‹ค์ œ๋กœ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š๋Š” ์ด์œ ๋ฅผ ์ฐพ์•˜๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ง‘์— ๋„์ฐฉํ•˜๋ฉด ๋ช‡ ์‹œ๊ฐ„ ํ›„์— ์ด ๋ฌธ์ œ๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ด๊ฒƒ์„ ์•Œ์•„๋‚ผ ๋•Œ๊นŒ์ง€ PR์„ ์ œ์ถœํ•˜๊ณ  ์‹ถ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
ํŽธ์ง‘: ๋”ฐ๋ผ์„œ ๊ตฌ๋งค๋ฅผ ์ •๋ ฌํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š” ์ด์œ ๋Š” ๊ท€ํ•˜๊ฐ€ ์ฟผ๋ฆฌํ•œ ๊ตฌ๋งค์— ๊ด€๊ณ„์—†์ด expires_date_ms๊ฐ€ ๋™์ผํ•˜๊ฒŒ ๋ฐ˜ํ™˜๋˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์—ฌ๋Ÿฌ๋ถ„๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๊ฐ€ ์•„๋‹™๋‹ˆ๊นŒ? ์•„๋‹ˆ๋ฉด ์ •๋ ฌ์ด ํ•„์š”ํ•œ ์•ฝ๊ฐ„์˜ ์ •๋ณด๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๊นŒ? ๋„ค ์ƒ๊ฐ์„ ๋งํ•ด๋ด. ๋‚˜๋Š” ๋ฌธ์„œ๊ฐ€ ๊ตฌ๋งค๊ฐ€ ์‹œ๊ฐ„์ˆœ์œผ๋กœ ์ •๋ ฌ๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์„ ์—ฌ์ „ํžˆ ๋ถ„๋ช…ํžˆ ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๋ฐ ๋™์˜ํ•˜์ง€๋งŒ, ๋‚ด๊ฐ€ ๋งํ•  ์ˆ˜ ์žˆ๋Š” ํ•œ ๋งŒ๋ฃŒ ๋‚ ์งœ๋ฅผ ์–ป๊ธฐ ์œ„ํ•ด ์ •๋ ฌ์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@kevinEsherick ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค! ์ œ ๊ฒฝ์šฐ์—๋Š” ๊ตฌ๋งค ๋˜๋Š” ๊ฐฑ์‹  ๊ธฐ๋ก์ด ์ฃผ๋ฌธ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

@kevinEsherick @andrewzey ์ด ์†”๋ฃจ์…˜์— PR ๋ฅผ ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ๊ณ ํ†ต๋ฐ›๋Š” ๋‹ค๋ฅธ ์ด๋“ค๊ณผ ๋‚˜๋ˆ„๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค.

๋ฌผ๋ก  ๊ณต๊ฐœ ์•ฑ ์ถœ์‹œ๊ฐ€ ์™„๋ฃŒ๋˜๋Š” ์ฆ‰์‹œ ์ค€๋น„ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค =).

๋‚˜๋Š” ๋‚˜์˜ ๋งˆ์ง€๋ง‰ ์ฝ”๋ฉ˜ํŠธ์— ๊ด€ํ•ด ๋” ๋งŽ์€ ๋Œ€ํ™”๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์žˆ์—ˆ๋‹ค. ํ•ด๊ฒฐ๋˜์ง€ ์•Š์€ ๋ช‡ ๊ฐ€์ง€ ๋ฌธ์ œ๋ฅผ ์ œ๊ธฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฐธ๊ณ ๋กœ ์œ„๋ฅผ ๋ด์ฃผ์„ธ์š”. ์ผ๋‹จ ์šฐ๋ฆฌ๊ฐ€ ๊ทธ๊ฒƒ์„ ์ •๋ฆฌํ•˜๋ฉด ์šฐ๋ฆฌ ์ค‘ ํ•œ ๋ช…์ด PR์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@kevinEsherick ์•„ ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ๋†“์ณค์Šต๋‹ˆ๋‹ค.

expires_date_ms ๋Š” ๊ฐ ๊ฐฑ์‹  ์˜์ˆ˜์ฆ์—์„œ ํ™•์‹คํžˆ ๊ณ ์œ ํ•ฉ๋‹ˆ๋‹ค. ์ œ๊ฐ€ ์ž˜๋ชป ์ดํ•ดํ•œ ๊ฒƒ์€ ์•„๋‹๊นŒ์š”? ๋””์ฝ”๋”ฉ๋œ ๊ฐ ์˜์ˆ˜์ฆ์— ์ฒจ๋ถ€๋œ ๊ฐฑ์‹  ์˜์ˆ˜์ฆ ๋ฐฐ์—ด์€ ์–ด๋–ค ์˜์ˆ˜์ฆ์ด ์„ ํƒ๋˜์—ˆ๋Š”์ง€์— ๊ด€๊ณ„์—†์ด ๋™์ผํ•˜๋ฏ€๋กœ ํ•„์š”ํ•˜์ง€ ์•Š์€ ๋‹ค์Œ(๋‚ด ์˜ˆ์—์„œ)์ด ์˜ณ์„ ์ˆ˜ ์žˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

const sortedAvailablePurchases = availablePurchases.sort(
      (a, b) => b.transactionDate - a.transactionDate
    );

๊ทธ๋Ÿฌ๋‚˜ ๋‚˜๋Š” ์ •ํ™•ํ•˜๋ ค๊ณ  ๋…ธ๋ ฅํ•˜๊ณ  Apple ๋ฌธ์„œ์—์„œ ์‹ค์ œ API ์‘๋‹ต์˜ ๊ฐ€๋Šฅํ•œ ๋ถˆ์ผ์น˜๋ฅผ ๋ณด์™„ํ•˜๊ธฐ ์œ„ํ•ด ๊ทธ๋ ‡๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ž๋™ ๊ฐฑ์‹  ๊ตฌ๋…์— ๋Œ€ํ•ด ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋Š” ์ด ์˜์ˆ˜์ฆ ์ˆ˜๋ฅผ ๊ณ ๋ คํ•  ๋•Œ ์ •๋ ฌ ์ž‘์—…์ด ๋งค์šฐ ๋น„์šฉ์ด ๋งŽ์ด ๋“ค ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

PR์— ETA๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ? ์ด ํŒจํ‚ค์ง€ ๋Š” ์ด ๊ฒฝ์šฐ๋ฅผ ์ „ํ˜€ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—

@schhumannd PR์ด ๋ฌธ์„œ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฒƒ์ผ ๋ฟ react-native-iap ๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ตœ๊ทผ ์ถœ์‹œ๋œ ์•ฑ์—์„œ ๋‚ด ์ฝ”๋“œ ์Šค๋‹ˆํŽซ์ด ์‹ค์ œ๋กœ ๋ฌธ์ œ ์—†์ด ์ž‘๋™ํ•จ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค. https://itunes.apple.com/us/app/rp-diet/id1330041267?ls=1&mt=8

ํŽธ์ง‘ ํ™•์‹คํžˆ PR์„ ํ•˜๊ฒ ์ง€๋งŒ(Trello์˜ "Give back to OSS" ๋ชฉ๋ก์— ์žˆ์Œ), ๋ธ”๋ž™ ํ”„๋ผ์ด๋ฐ์ด ์ฃผ๋ง ๊ด‘๋ž€ ์ดํ›„ ๐Ÿ˜„

@schhumannd ์ €๋Š” @andrewzey๊ฐ€ ๋งํ•œ ๋‘ ๋ฒˆ์งธ์ž…๋‹ˆ๋‹ค. PR์ด ๋งํ•˜๋Š” ๋ชจ๋“  ๊ฒƒ์ด ์ด ๊ฒŒ์‹œ๋ฌผ์— ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๊ณ  ํ–ˆ์ง€๋งŒ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•  ์ž‘์—…์„ ์ •ํ™•ํžˆ ์ •๋ฆฌํ•˜๋Š” ๋ฐ ์‹œ๊ฐ„์ด ๊ฑธ๋ ธ๊ณ  ์—ฌํ–‰๊ณผ ๋ฐ”์œ ์‹œ์ž‘ ์‹œ๊ฐ„์ด ๋’ค์„ž์—ฌ์„œ ์•„์ง ํ™๋ณดํ•  ์‹œ๊ฐ„์ด ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ์—ฌ์ „ํžˆ ๊ณง ํ•  ๊ณ„ํš์ด์ง€๋งŒ, ์›ํ•˜๋Š” ๊ฒฝ์šฐ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ๊ทธ ๋™์•ˆ ๋‹จ๊ณ„๋ฅผ ๋ฐŸ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  @andrewzey ์ถ•ํ•˜ํ•ฉ๋‹ˆ๋‹ค ! ๋ฉ‹์ ธ๋ณด์ด๋„ค์š” ๋‹ค์šด๋ฐ›๊ฒ ์Šต๋‹ˆ๋‹ค!

์ด ์Šค๋ ˆ๋“œ๊ฐ€ iOS์—์„œ ์ž๋™ ๊ฐฑ์‹  ๊ตฌ๋…์„ ๊ด€๋ฆฌํ•˜๋Š” ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•์„ ์ •ํ™•ํ•˜๊ฒŒ ๋ฌธ์„œํ™”ํ–ˆ๋Š”์ง€ ํ™•์‹ ํ•  ์ˆ˜ ์—†์ง€๋งŒ ๋Œ€์‹  ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์˜ํ•ด ๋…ธ์ถœ๋œ API์— ๋”ฐ๋ผ "ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•" ์›Œํฌํ”Œ๋กœ๋ฅผ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์€ ์ „์ ์œผ๋กœ ๋‚ด ์ถ”์ธก์ด๋ฉฐ ๋‚ด๊ฐ€ ํ‹€๋ฆด ์ˆ˜ ์žˆ์Œ์„ ์ž˜ ์•Œ๊ณ  ์žˆ์œผ๋ฏ€๋กœ ์•„๋ž˜์— ์„ค๋ช…๋œ ๊ด€์ฐฐ์— ๋Œ€ํ•œ ํ”ผ๋“œ๋ฐฑ์„ ๋ฐ›๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

๋‚˜๋Š” ๋ช‡ ์ฃผ ๋™์•ˆ React Native์—์„œ ์ž๋™ ๊ฐฑ์‹  ๊ตฌ๋…์„ ๊ด€๋ฆฌํ•˜๋Š” ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•์„ ์—ฐ๊ตฌํ–ˆ์ง€๋งŒ ์•„๋ฌด ์†Œ์šฉ์ด ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๊ธฐ์–ด๋ฅผ ์ „ํ™˜ํ•˜๊ณ  Objective-C/Swift์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ตฌ๋…์„ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์—ฐ๊ตฌํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋‚˜์—๊ฒŒ ๋„ค์ดํ‹ฐ๋ธŒ StoreKit API์™€ ๋ฌธ์„œํ™”๋œ ์‚ฌ์šฉ๋ฒ•์„ ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๊ตฌํ˜„๋œ ๊ฒƒ๊ณผ ๋น„๊ตํ•  ์ˆ˜ ์žˆ๋Š” ์ฐธ์กฐ ์ง€์ ์„ ์ œ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ธ€์€ ๊ธฐ๋ณธ iOS ์•ฑ์—์„œ ๊ตฌ๋… ๊ตฌ๋งค, ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋ฐ ๋ณต์›์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์ง€๊ธˆ๊นŒ์ง€ ์ฐพ์€ ์ตœ๊ณ ์˜ ์„ค๋ช…์ž…๋‹ˆ๋‹ค. https://www.raywenderlich.com/659-in-app-purchases-auto -renewable-subscriptions-tutorial ๋ฐ ๋ชจ๋“  API๊ฐ€ react-native-iap ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๋™์•ˆ ๋‹ค๋ฅธ ์›Œํฌํ”Œ๋กœ๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ๋ณด๋Š” ์ฃผ์š” ์ฐจ์ด์ ์€ appStoreReceiptUrl ์ž…๋‹ˆ๋‹ค.

StoreKit์€ ๋‚ด ์ดํ•ด์— ๋”ฐ๋ผ iOS ์•ฑ์— ๋Œ€ํ•œ ํ˜„์žฌ ๊ตฌ๋งค/์˜์ˆ˜์ฆ ์ •๋ณด๋ฅผ ๋กœ๋“œํ•˜๋Š” ์˜ฌ๋ฐ”๋ฅธ ๋ฐฉ๋ฒ•์ธ appStoreReceiptUrl ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. react-native-iap ์ฝ”๋“œ์—์„œ ์ด๊ฒƒ์ด ์‚ฌ์šฉ๋˜๊ณ  ์ฐธ์กฐ๋˜๋Š” ๊ฒƒ์„

์ด ๊ตฌํ˜„ ์ฐจ์ด๊ฐ€ ๋ฌธ์ œ๊ฐ€ ์žˆ๋Š”์ง€ ์—ฌ๋ถ€๋Š” ํ™•์‹คํ•˜์ง€ ์•Š์ง€๋งŒ react-native-iap API๋Š” ๊ฒฐ๊ตญ getPurchaseHistory ์˜ํ•ด ํ˜ธ์ถœ๋˜๋Š” StoreKit์˜ restoreCompletedTransactions ์— ๋ถˆํ•„์š”ํ•œ ์ข…์†์„ฑ์„ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค getPurchaseHistory .

react-native-iap API๋Š” iOS์— ๋Œ€ํ•ด ๋‹ค์Œ ์›Œํฌํ”Œ๋กœ๋ฅผ ์ง€์›ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘ ์‹œ ํ˜„์žฌ ์•ฑ ์˜์ˆ˜์ฆ ๋กœ๋“œ ์‹œ๋„( appStoreReceiptUrl )
  • ์˜์ˆ˜์ฆ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐํšŒ ์ œ๊ณต
  • appStoreReceiptUrl ๊ฐ€ null์ธ ๊ฒฝ์šฐ ํ˜„์žฌ ์žฅ์น˜์—์„œ "๋ณต์›"์„ ์‹œ์ž‘ํ•œ ๋‹ค์Œ appStoreReceiptUrl ๊ฐ’์„ ์„ค์ •ํ•˜๊ณ  ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋…ผ๋ฆฌ๋ฅผ ๋‹ค์‹œ ์‹œ๋„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ƒ๊ฐ?

๋‹ค์‹œ ๋งํ•˜์ง€๋งŒ, ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๊ตฌํ˜„ ๋˜๋Š” ์ฐธ์กฐ๋œ ๊ธฐ๋ณธ ๊ตฌํ˜„์„ ์˜คํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@sellmeadog

๊ท€ํ•˜๊ฐ€ ๋งํฌํ•œ ๊ธฐ์‚ฌ์—์„œ๋„ ์–ธ๊ธ‰๋œ ์š”์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ : ํ”„๋กœ์ ํŠธ ์•ฑ์€ ์˜์ˆ˜์ฆ์„ ์ˆ˜๋ฝํ•˜๊ณ  Apple์˜ ์˜์ˆ˜์ฆ ํ™•์ธ ์„œ๋น„์Šค์— ์ง์ ‘ ์—…๋กœ๋“œํ•˜๋Š” SelfieService๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ํŠœํ† ๋ฆฌ์–ผ์ด ๊ตฌ๋… ์„ค์ •์— ์ง‘์ค‘ํ•˜๋„๋ก ํ•˜๋Š” "์น˜ํŠธ"์ž…๋‹ˆ๋‹ค. ์‹ค์ œ ์•ฑ์—์„œ๋Š” ์•ฑ์ด ์•„๋‹Œ ์›๊ฒฉ ์„œ๋ฒ„๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ „์ฒด ์ž๋™ ๊ฐฑ์‹  ๊ตฌ๋…์„ ํŒŒ์•…ํ•˜๋Š” ๋ฐ ์‹œ๊ฐ„์ด ๊ฑธ๋ ธ์ง€๋งŒ ๋‚ด๊ฐ€ ์ดํ•ดํ•˜๋Š” ๊ฐ€์žฅ ์ข‹์€ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•์€ ๊ณ ์œ ํ•œ ๋ฐฑ์—”๋“œ๋ฅผ ๋‹จ์ผ ์†Œ์Šค๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

  • ๋ชจ๋“  ๊ฑฐ๋ž˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฆ‰์‹œ ๋ฐฑ์—”๋“œ๋กœ ์ „์†ก
  • ์ „์†ก ๋ฌธ์ œ์˜ ๊ฒฝ์šฐ ์žฌ์ „์†ก ์ˆ˜๋‹จ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค(์˜ˆ: ๊ตฌ๋งค ๋ณต์›).
  • ์ฃผ๊ธฐ์ ์œผ๋กœ(์˜ˆ: ํ•˜๋ฃจ์— ํ•œ ๋ฒˆ) ๋ฐฑ์—”๋“œ์— ์ €์žฅ๋œ ๋ชจ๋“  ์˜์ˆ˜์ฆ ํ™•์ธ
  • ์•ฑ์€ ์˜ˆ๋ฅผ ๋“ค์–ด ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋ฐ˜ํ™˜ํ•  ๋•Œ ๊ตฌ๋… ์ƒํƒœ์— ๋Œ€ํ•ด ๋ฐฑ์—”๋“œ๋ฅผ ์ฟผ๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

๋‚˜์—๊ฒŒ ์ด๊ฒƒ์€ ๊ตฌ๋…์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์œ ์ผํ•œ ์ œ์ •์‹ ์ธ ๋ฐฉ๋ฒ•์ฒ˜๋Ÿผ ๋ณด์˜€๋‹ค. ๋˜ํ•œ ํ•œ๋™์•ˆ ์ƒ์—…์ ์œผ๋กœ ์ž๋™ ๊ฐฑ์‹  ๊ตฌ๋…์„ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž์™€ ์ด ํ”„๋กœ์„ธ์Šค๋ฅผ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ OP ์งˆ๋ฌธ์— ๋Œ€ํ•œ ๋‚ด ๋Œ€๋‹ต์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ํ”„๋ก ํŠธ์—”๋“œ๊ฐ€ ์•„๋‹Œ ๋ฐฑ์—”๋“œ์—์„œ ์ฃผ๊ธฐ์ ์œผ๋กœ ๊ตฌ๋…์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

@schumand

๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ์ดํ•ดํ•˜๊ฑฐ๋‚˜ ์ด์•ผ๊ธฐํ•˜๊ณ  ์žˆ๋Š”์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค.

์˜์ˆ˜์ฆ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋Š” ์„œ๋ฒ„์—์„œ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Apple ์ž์ฒด ๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด :

์•ฑ์—์„œ App Store ์„œ๋ฒ„ /verifyReceipt ์—”๋“œํฌ์ธํŠธ๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค.

๋‚˜๋Š” ์ฐธ์กฐ๋œ ๊ธฐ์‚ฌ์˜ "์น˜ํŠธ"๋ฅผ ๋ฐฑ์—”๋“œ ์„œ๋ฒ„์— ์œ„์ž„ํ•˜๋Š” ๋Œ€์‹  ๊ฐ„๊ฒฐ์„ฑ์„ ์œ„ํ•ด ์•ฑ์—์„œ ์ง์ ‘ /verifyReceipt ๋Œ€ํ•œ ์ƒ˜ํ”Œ ์•ฑ์˜ ์ง์ ‘ ํ˜ธ์ถœ๋กœ ํ•ด์„ํ–ˆ์Šต๋‹ˆ๋‹ค.

Apple/App Store๋Š” ์ง„์‹ค์˜ ๋‹จ์ผ ์ถœ์ฒ˜์ด๋ฉฐ ๊ทธ๋ž˜์•ผ๋งŒ ํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. Apple์€ ๊ถ๊ทน์ ์œผ๋กœ ๊ตฌ๋งค, ๊ฐฑ์‹ , ์ทจ์†Œ ๋“ฑ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ๊ทธ์— ๋”ฐ๋ผ ์˜์ˆ˜์ฆ์„ ์ƒ์„ฑํ•˜๊ณ  StoreKit API๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

react-native-iap ๋Š” ๋ฌธ์ œ ์—†์ด ๊ตฌ๋งค ๋ฐ ๊ตฌ๋งค ๋ณต์›์„ ์ฒ˜๋ฆฌํ•˜์ง€๋งŒ ์—ฌ์ „ํžˆ Apple์˜ ๋ฌธ์„œํ™”๋œ ์˜์ˆ˜์ฆ ์•ก์„ธ์Šค ๋ฐฉ๋ฒ•์ธ appStoreReceiptUrl ์—์„œ ๋กœ์ปฌ๋กœ ์ œ๊ณต๋˜๋Š” App Store ์˜์ˆ˜์ฆ ๋ฐ์ดํ„ฐ์— ์•ก์„ธ์Šคํ•˜๋Š” ๋ฐ ๊ฒฉ์ฐจ๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ :

์˜์ˆ˜์ฆ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒ€์ƒ‰ํ•˜๋ ค๋ฉด NSBundle ์˜ appStoreReceiptURL ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์•ฑ์˜ ์˜์ˆ˜์ฆ์„ ์ฐพ์Šต๋‹ˆ๋‹ค...

Apple ๋ฌธ์„œ๋ฅผ ์ฝ๊ณ  ๊ธฐ๋ณธ Object-C/Swift ๊ตฌํ˜„์„ ์ฐธ์กฐํ•˜์—ฌ ์ดํ•ดํ•œ ์›Œํฌํ”Œ๋กœ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ๊ตฌ๋งคํ•  ์ œํ’ˆ ๋กœ๋“œ
  • ์ œํ’ˆ ๊ตฌ๋งค(์†Œ๋ชจ, ๋น„์†Œ๋ชจ, ๊ตฌ๋…, ์ƒ๊ด€์—†์Œ)
  • ํ•ด๋‹น ๊ตฌ๋งค์— ๋Œ€ํ•œ ์˜์ˆ˜์ฆ์€ appStoreReceiptUrl ์˜ํ•ด StoreKit appStoreReceiptUrl ์— ๋กœ์ปฌ๋กœ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค.
  • ์•ฑ์€ ํ•„์š”์— ๋”ฐ๋ผ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์œ„ํ•ด ์˜์ˆ˜์ฆ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐฑ์—”๋“œ ์„œ๋ฒ„(App Store /verifyReceipt ์•„๋‹Œ ์ง์ ‘)๋กœ ๋ณด๋‚ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • appStoreReceiptUrl ๋Š” ์ทจ์†Œ ๋ฐ/๋˜๋Š” ๊ฐฑ์‹ ์ด ๋ฐœ์ƒํ•  ๋•Œ๋งˆ๋‹ค StoreKit appStoreReceiptUrl ๋กœ ์—…๋ฐ์ดํŠธ๋ฉ๋‹ˆ๋‹ค(์ ์ ˆํ•œ StoreKit ๋Œ€๋ฆฌ์ธ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋“ฑ๋ก๋˜์—ˆ๋‹ค๊ณ  ๊ฐ€์ •).
  • appStoreReceiptUrl ๊ฐ€ null์ธ ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž๊ฐ€ ์ƒˆ ์žฅ์น˜ ๋“ฑ์„ ์‚ฌ์šฉ ์ค‘์ด๊ธฐ ๋•Œ๋ฌธ์— Apple/AppStore์—์„œ ํ˜„์žฌ ์˜์ˆ˜์ฆ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒ€์ƒ‰ํ•˜๊ณ  StoreKit ๊ฐ€ appStoreReceiptUrl ์—์„œ ๋กœ์ปฌ๋กœ ์œ ์ง€ํ•˜๋Š” ๋ณต์› ๊ตฌ๋งค ์›Œํฌํ”Œ๋กœ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค

๋‹ค์‹œ react-native-iap ๋Š” appStoreReceiptUrl ์—์„œ ์˜์ˆ˜์ฆ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋“œํ•˜๋Š” ๊ฒƒ์„ ์ œ์™ธํ•˜๊ณ  ์ด ๋ชจ๋“  ๊ฒƒ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ํ˜„์ง€ ์˜์ˆ˜์ฆ์„ ์กฐํšŒํ•˜๋Š” getReceipt ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ตฌ๋… ๋ฐ/๋˜๋Š” ๊ธฐํƒ€ ๊ตฌ๋งค๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ถ€๋‹ด์„ ๋œ ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์• ํ”Œ์€ ์ด๊ฒƒ์„ ์ƒ๊ฐ๋ณด๋‹ค ํ›จ์”ฌ ๋” ์–ด๋ ต๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค.

appStoreReceiptUrl ๋Š” ์ทจ์†Œ ๋ฐ/๋˜๋Š” ๊ฐฑ์‹ ์ด ๋ฐœ์ƒํ•  ๋•Œ๋งˆ๋‹ค StoreKit appStoreReceiptUrl ๋กœ ์—…๋ฐ์ดํŠธ๋ฉ๋‹ˆ๋‹ค(์ ์ ˆํ•œ StoreKit ๋Œ€๋ฆฌ์ธ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋“ฑ๋ก๋˜์—ˆ๋‹ค๊ณ  ๊ฐ€์ •).

์ด๊ฒƒ์€ ๋‚ด๊ฐ€ ์ด ์ ‘๊ทผ ๋ฐฉ์‹์— ๋Œ€ํ•ด ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๋ฌธ์ œ๋ฅผ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์ธํ„ฐ๋„ท ์—ฐ๊ฒฐ์ด ์ž‘๋™ํ•˜๋Š” ์ƒํƒœ์—์„œ ์ดํ›„์— ์•ฑ์„ ์—ด์ง€ ์•Š์œผ๋ฉด ๊ฐฑ์‹ /์ทจ์†Œ/๋งŒ๋ฃŒ ๋“ฑ์— ๋Œ€ํ•œ ์•Œ๋ฆผ์„ ๋ฐ›์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค.

๊ทธ ์™ธ์—๋Š” ๋‚ด๊ฐ€ ์„ค๋ช…ํ•œ ์ ‘๊ทผ ๋ฐฉ์‹์— ๋Œ€ํ•œ ์ข‹์€ ๋Œ€์•ˆ์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์–ด๋–ค ๊ฒƒ์ด ํ”„๋กœ๋•์…˜ ์•ฑ์— ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค.

@sellmeadog ๋‚˜๋Š” ๊ทธ๊ฒƒ์ด ๋‚ด๊ฐ€ ์—ฌ๊ธฐ์— ๊ฒŒ์‹œํ•œ ๊ฒƒ๊ณผ ๊ฐ™์€ ์ƒ๊ฐ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค: #356
ํ˜„์ง€์—์„œ ์˜์ˆ˜์ฆ์„ ํ™•์ธํ•˜๋Š” ๊ฒƒ๋„ ํ•œ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด๋ผ๊ณ  Apple์€ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋ชจ๋“  ์„œ๋ฒ„/๋„คํŠธ์›Œํฌ ์š”์ฒญ๊ณผ ์™„์ „ํžˆ ๋…๋ฆฝ์ ์ž…๋‹ˆ๋‹ค.

๋„ค. "๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ์—†์Œ - ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์—†์Œ"๊ณผ ๊ด€๋ จ๋œ ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
๊ทธ๋Ÿฌ๋‚˜ ์ด์— ๋Œ€ํ•œ ์‚ฌ์šฉ ์‚ฌ๋ก€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
๋ชจ๋“  ์•ฑ์„ ์‹คํ–‰ํ•  ๋•Œ๋งˆ๋‹ค ์˜์ˆ˜์ฆ์„ ๋น ๋ฅด๊ฒŒ ํ™•์ธํ•˜๊ณ  UserDefaults ๋˜๋Š” Keychain ๋ฅผ ํ†ตํ•ด ์ธ์•ฑ ๊ตฌ๋งค๋ฅผ ์ถ”์ ํ•˜๊ณ  ์‹ถ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. StoreKit ์ด๋ฏธ ์ €์žฅ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

IOS ์‚ฌ์šฉ์ž๊ฐ€ getAvailablePurchases์™€ ๊ฐ™์€ ๊ฒƒ์„ ํ†ตํ•ด itunes ๊ณ„์ •์— ๋กœ๊ทธ์ธํ•˜๋ผ๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ํ‘œ์‹œํ•˜์ง€ ์•Š๊ณ  ๊ตฌ๋…์„ ๊ฐฑ์‹ ํ•˜๊ฑฐ๋‚˜ ์ทจ์†Œํ–ˆ๋Š”์ง€ ์–ด๋–ป๊ฒŒ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๊นŒ?

์˜ˆ๋ฅผ ๋“ค์–ด:
๊ตฌ๋…์„ ์ฒ˜์Œ ๊ตฌ๋งคํ•  ๋•Œ ์˜์ˆ˜์ฆ์„ ํ™•์ธํ•˜๊ณ  ๋งŒ๋ฃŒ ๋‚ ์งœ๋ฅผ Firebase ํ”„๋กœํ•„์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น Firebase ๋…ธ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ™œ์„ฑํ™”๋œ ๊ตฌ๋…์ด ์žˆ๋Š”์ง€ ์‹œ์ž‘ ์‹œ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ๋ฌธ์ œ๋Š” ๋งŒ๋ฃŒ์ผ์ด ์ง€๋‚˜๊ณ  ์ƒˆ๋กœ์šด ์ฒญ๊ตฌ ๊ธฐ๊ฐ„์ด ์‹œ์ž‘๋  ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ž๋™ ๊ฐฑ์‹  ๋˜๋Š” ์ทจ์†Œ๋˜์—ˆ๋Š”์ง€ ์–ด๋–ป๊ฒŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด ์šฐ๋ฆฌ๋Š” getAvailablePurchases๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์—ˆ์ง€๋งŒ ๋ณต์› ๋ฒ„ํŠผ์ฒ˜๋Ÿผ itunes ๋กœ๊ทธ์ธ์„ ํ”„๋กฌํ”„ํŠธํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋”์ฐํ•œ UI์ž…๋‹ˆ๋‹ค.

์ €๋„ ๊ฐ™์€๊ฒŒ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค.

์•ฑ์—์„œ๋Š” ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด Apple๊ณผ ์ง์ ‘ ํ†ต์‹ ํ•˜๋Š” ์„œ๋ฒ„ ์ธก API๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ ์ด ํ•ญ๋ชฉ์ด ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ตœ์ดˆ ๊ตฌ๋งค ํ›„ ์˜์ˆ˜์ฆ ๋ฐ์ดํ„ฐ๋Š” ๋‹น์‚ฌ ์„œ๋ฒ„์— ์ €์žฅ๋˜๊ณ  ๋‚˜์ค‘์— ์—…๋ฐ์ดํŠธ๋œ ์ƒํƒœ๋ฅผ ์š”์ฒญํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ๋‚˜๋Š” ์ด๊ฒƒ์ด ์žฅ์น˜ ์ธก๋ฉด์—์„œ ์—ฌ์ „ํžˆ ๊ฐ€๋Šฅํ•ด์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ด์— ๋Œ€ํ•œ ์ „๋ฌธ๊ฐ€๋Š” ์•„๋‹ˆ์ง€๋งŒ ๋‚ด๊ฐ€ ์ดํ•ดํ•˜๋Š” ๋ฐ”์— ๋”ฐ๋ฅด๋ฉด ์•ฑ์€ ๊ฐฑ์‹ ์ด ๋ฐœ์ƒํ•  ๋•Œ๋งˆ๋‹ค OS์—์„œ ์—…๋ฐ์ดํŠธ๋œ ๊ฑฐ๋ž˜/์˜์ˆ˜์ฆ์„ ๋ฐ›์Šต๋‹ˆ๋‹ค. ์ด ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์•ฑ ์‹คํ–‰ ์‹œ ์ด๋Ÿฌํ•œ ์˜์ˆ˜์ฆ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. (Xcode์—์„œ ์‹คํ–‰ํ•  ๋•Œ ์—ฌ๋Ÿฌ ํŠธ๋žœ์žญ์…˜์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ด๋Š” ๋งŽ์€ ๋กœ๊ทธ๋ฅผ ๋ด…๋‹ˆ๋‹ค.) ํ•˜์ง€๋งŒ ์ œ๊ฐ€ ์•Œ ์ˆ˜ ์žˆ๋Š” ๋ฐ”์— ๋”ฐ๋ฅด๋ฉด JS ์ฝ”๋“œ์—์„œ ์ด๋Ÿฌํ•œ ์ด๋ฒคํŠธ๋ฅผ ํ™œ์šฉํ•  ๋ฐฉ๋ฒ•์ด ์—†์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์— ๋Œ€ํ•ด ๋” ๋งŽ์€ ์ •๋ณด๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

@csumrell @curiousdustin ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ ์ด ๋™์ž‘์ด ํ™•์ธ๋˜์—ˆ์Šต๋‹ˆ๊นŒ? ์žฅ์น˜์— ์ƒŒ๋“œ๋ฐ•์Šค๊ฐ€ ์•„๋‹Œ ๋‹ค๋ฅธ ๊ณ„์ •์ด ์žˆ์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ƒŒ๋“œ๋ฐ•์Šค ๊ณ„์ •์— ๋กœ๊ทธ์ธํ•ด์•ผ ํ•˜๋Š” ์ƒŒ๋“œ๋ฐ•์Šค ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด ํ”„๋กœ๋•์…˜์—์„œ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ผ๋ฐ˜ ๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธํ•˜๊ณ  ์ด ๋ชจ๋“ˆ์€ ๋กœ๊ทธ์ธ์„ ์š”๊ตฌํ•˜์ง€ ์•Š๊ณ  getPurchases๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. getAvailablePurchases() ๊ฐ€ ํ”„๋กœ๋•์…˜์—์„œ ์•”ํ˜ธ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์œ ๋ฐœํ•˜๋Š”์ง€ ํ™•์ธํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

๋ฌธ์„œ์—์„œ ์ผ๋ฐ˜์ ์œผ๋กœ ๊ตฌ๋งค ๋ณต์›์ด๋ผ๊ณ  ํ•˜๋Š” ๊ธฐ๋Šฅ์— ์‚ฌ์šฉ๋˜๋Š” ์ด ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ๋Ÿด ๊ฒƒ์ด๋ผ๊ณ  ๊ฐ€์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚ด ๊ฒฝํ—˜์— ๋”ฐ๋ฅด๋ฉด ์ด ๋ฐฉ๋ฒ•์€ ์ ์–ด๋„ iOS์—์„œ ์ธ์ฆ์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

@hyochan @JJMoon ์ด๊ฒƒ๋„ ์ข€ ๋”

@curiousdustin ์˜ˆ , ์ผ๋ฐ˜ iTunes ๊ณ„์ •์—์„œ ๋กœ๊ทธ์•„์›ƒํ•˜๊ณ  ์ƒŒ๋“œ๋ฐ•์Šค ๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธํ•˜๋ฉด ์ด ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ ๋” ์ด์ƒ ๋กœ๊ทธ์ธํ•˜๋ผ๋Š” ๋ฉ”์‹œ์ง€๊ฐ€ ํ‘œ์‹œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋‚ด ์ถ”์ธก/ํฌ๋ง์€ ์‚ฌ์šฉ์ž๊ฐ€ ์‚ฌ์šฉ ์ค‘์ธ ์ƒŒ๋“œ๋ฐ•์Šค ๊ณ„์ •์ด ์—†๋Š” ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋ฒ ํƒ€ ํ…Œ์ŠคํŠธ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์žฅ์น˜์— ์ƒŒ๋“œ๋ฐ•์Šค ๊ณ„์ •์„ ์„ค์ •ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ํ”„๋กœ๋•์…˜๊ณผ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์ž‘๋™ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๊ฐœ๋ฐœ ์žฅ์น˜์˜ ๋ฌธ์ œ์ผ ๋ฟ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋‚ด์ผ ๋ฒ ํƒ€ ์‚ฌ์šฉ์ž์™€ ํ•จ๊ป˜ ์ด๊ฒƒ์„ ํ…Œ์ŠคํŠธํ•˜๊ณ  ๋‚ด๊ฐ€ ์ฐพ์€ ๊ฒƒ์„ ์•Œ๋ ค ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.
ํŽธ์ง‘: ์ƒŒ๋“œ๋ฐ•์Šค๊ฐ€ ์•„๋‹Œ ์žฅ์น˜์—์„œ ์ด๋Ÿฌํ•œ ์ผ์ด ๋ฐœ์ƒํ•จ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋”์ฐํ•œ UX์ด๋ฉฐ ์ƒ๋‹นํžˆ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ˆ„๊ตฌ, ์–ด๋–ค ์•„์ด๋””์–ด๋ผ๋„?

๋”ฐ๋ผ์„œ ์•ฑ์—์„œ App Store ์„œ๋ฒ„ /verifyReceipt ์—”๋“œํฌ์ธํŠธ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ž์ฒด ์„œ๋ฒ„์—์„œ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•  ๋•Œ ์›๋ž˜์˜ ์ฒซ ๋ฒˆ์งธ ๊ตฌ๋งค ์˜์ˆ˜์ฆ๊ณผ ํ•จ๊ป˜ ํ•ด๋‹น ์‚ฌ์šฉ์ž์— ๋Œ€ํ•œ ์ตœ์‹  ์—…๋ฐ์ดํŠธ ์ •๋ณด๋ฅผ ํ•ญ์ƒ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

๋กœ์ปฌ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉด ํ•ญ์ƒ ์ตœ์‹  ์ •๋ณด๋„ ๋ฐ˜ํ™˜๋˜๋Š”์ง€ ์•„๋Š” ์‚ฌ๋žŒ์ด ์žˆ์Šต๋‹ˆ๊นŒ? ์•„๋‹ˆ๋ฉด ํ•ด๋‹น ์˜์ˆ˜์ฆ์— ๋Œ€ํ•ด์„œ๋งŒ ๋กœ์ปฌ์—์„œ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๊นŒ?

๋˜ํ•œ Apple๊ณผ ํ•จ๊ป˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•˜๊ธฐ ์œ„ํ•ด ์ž์ฒด ์„œ๋ฒ„๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์กฐ์–ธ์ด ์žˆ๋Š” ์‚ฌ๋žŒ์ด ์žˆ์Šต๋‹ˆ๊นŒ? ๋‚˜๋Š” ๊ทธ๊ฒƒ์— ๋Œ€ํ•œ ๊ฒฝํ—˜์ด ์—†๋‹ค

@csumrell getPurchaseHistory๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ์ •๋ ฌํ•˜์—ฌ ์ตœ๊ทผ ๊ตฌ๋งค์— ๋Œ€ํ•œ ์˜์ˆ˜์ฆ์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์œ„์˜ ์ฃผ์„์€ ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ž์„ธํžˆ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—๋Š” ์ตœ์‹  ์ •๋ณด๊ฐ€ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์„œ๋กœ๋ฅผ ๋„์šธ ์ˆ˜ ์žˆ๋„๋ก ์„œ๋ฒ„์—์„œ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•˜๋Š” ์˜ต์…˜์„ ํƒ์ƒ‰ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ์–ด๋–ป๊ฒŒ ๊ฐ”๋Š”์ง€ ๋“ฃ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

@kevinEsherick ์˜ˆ, ํ•˜์ง€๋งŒ getPurchaseHistory๋Š” Itunes ๋กœ๊ทธ์ธ ํŒ์—…์„ ํŠธ๋ฆฌ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์ฒ˜์Œ ๊ตฌ๋งคํ•  ๋•Œ ์›๋ณธ ์˜์ˆ˜์ฆ์„ ๋กœ์ปฌ ์ €์žฅ์†Œ์— ์ €์žฅํ•˜๋Š” ๊ฒƒ์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ getPurchaseHistory๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋‹ค์Œ ์ฒญ๊ตฌ๋ฅผ ํ™•์ธํ•˜๋Š” ๋Œ€์‹  ๋กœ์ปฌ์—์„œ ๊ฒ€์ฆ๋œ validateReceiptIos(originalReceipt)๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ validateReceiptIos๊ฐ€ Apple ์„œ๋ฒ„ /verifyReceipt์™€ ๊ฐ™์€ ์ตœ์‹  ํŠธ๋žœ์žญ์…˜์„ ๋ฐ˜ํ™˜ํ•˜๋Š”์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@csumrell ๊ฐ“์ฑ  . ๋น ๋ฅด๊ณ  ์‰ฝ๊ฒŒ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•ˆ์จ๋ดค์–ด? ์•„๋‹ˆ๋ผ๋ฉด ์˜ค๋Š˜ ๋Šฆ๊ฒŒ ํ™•์ธํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
ํŽธ์ง‘: @csumrell validateReceiptIos๋Š” ์‹ค์ œ๋กœ ํ•ด๋‹น ๊ตฌ๋งค์— ๋Œ€ํ•œ ์ตœ์‹  ์ž๋™ ๊ฐฑ์‹ ์„ ์ถ”์ ํ•˜๋ฏ€๋กœ ์ด๊ฒƒ์ด ์œ ํšจํ•œ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ด๊ฒƒ์ด iTunes ๋กœ๊ทธ์ธ ํ”„๋กฌํ”„ํŠธ๋ฅผ ํ”ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ตฌ๋…์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์œ„์—์„œ ์ œ์•ˆํ•œ ๋ฐฉ๋ฒ•๋ณด๋‹ค ๋‚ซ๋‹ค๊ณ  ๋งํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค. ๋‹จ์ ์€ ์˜์ˆ˜์ฆ์„ ๋กœ์ปฌ์— ์ €์žฅํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋„ˆ๋ฌด ๋ฒˆ๊ฑฐ๋กญ์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ๋ณด์•ˆ์ƒ์˜ ์ด์œ ๋กœ ๋˜๋Š” ๋Œ€์šฉ๋Ÿ‰(์ตœ๋Œ€ 60,000์ž)์ด๋ผ๋Š” ์‚ฌ์‹ค ๋•Œ๋ฌธ์— ์ด๋Ÿฌํ•œ ์˜์ˆ˜์ฆ์ด ๋ฐฑ์—”๋“œ๋กœ ๊ฐ€๋Š” ๊ฒƒ์„ ์›ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๊ตฌ๋…ํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์•„์›ƒํ•˜๊ฑฐ๋‚˜ ์ƒˆ ์žฅ์น˜๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์‹œ์ž‘ํ•˜๋ฉด ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ตœ์‹  ๊ตฌ๋งค ์˜์ˆ˜์ฆ์„ ๋ฐ›์œผ๋ ค๋ฉด ๋กœ๊ทธ์ธํ•˜๋ผ๋Š” ๋ฉ”์‹œ์ง€๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์œผ๋กœ ํ™•์ธํ•œ ์‚ฌ์šฉ์ž์—๊ฒŒ๋งŒ ๋ฉ”์‹œ์ง€๋ฅผ ํ‘œ์‹œํ•˜๋„๋ก ๊ตฌ๋… ์ƒํƒœ๋ฅผ ๋‚ด๋ถ€์ ์œผ๋กœ ์ €์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@hyochan ์€ ์ด ๋ฌธ์ œ์— $20.00๋ฅผ ๊ธฐ๋ถ€ํ–ˆ์Šต๋‹ˆ๋‹ค. IssueHunt์—์„œ ํ™•์ธํ•˜์„ธ์š”.

@hyochan ๊ตฌ์ฒด์ ์œผ๋กœ ์–ด๋–ค ์†”๋ฃจ์…˜์— ์ž๊ธˆ์„ ์ง€์›ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ? ์šฐ๋ฆฌ๋Š” ์ด ์Šค๋ ˆ๋“œ์—์„œ ๋ช‡ ๊ฐ€์ง€๋ฅผ ์ œ๊ณตํ–ˆ์œผ๋ฉฐ ๋‚ด ์ตœ๊ทผ ์˜๊ฒฌ์ด ๊ฐ€์žฅ ์ ์€ ๋ฌธ์ œ๋กœ ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•œ ์†”๋ฃจ์…˜์„ ์ œ๊ณตํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. README์— ๋Œ€ํ•œ ํ•˜๋‚˜์˜ ๊ฐ„๊ฒฐํ•œ PR๋กœ ์ด๋Ÿฌํ•œ ์†”๋ฃจ์…˜์„ ์ˆ˜์ง‘ํ•˜๊ธฐ ์œ„ํ•ด ๋ˆ„๊ตฐ๊ฐ€์—๊ฒŒ ์ž๊ธˆ์„ ์ง€์›ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ? ์•„๋‹ˆ๋ฉด ์—ฌ์ „ํžˆ ํ•ด๊ฒฐ๋˜๊ธฐ๋ฅผ ์›ํ•˜๋Š” ์‹ค์ œ ์ฝ”๋“œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

@kevinEsherick

README์— ๋Œ€ํ•œ ํ•˜๋‚˜์˜ ๊ฐ„๊ฒฐํ•œ PR๋กœ ์ด๋Ÿฌํ•œ ์†”๋ฃจ์…˜์„ ์ˆ˜์ง‘ํ•˜๊ธฐ ์œ„ํ•ด ๋ˆ„๊ตฐ๊ฐ€์—๊ฒŒ ์ž๊ธˆ์„ ์ง€์›ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๊นŒ?

์˜ˆ. ์ด๊ฒƒ์€ ์ ˆ๋Œ€์ ์œผ๋กœ ๋งž์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ issue hunt ๊ฐ€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค. ๋‚ด ๋‹ค์Œ ๊ณ„ํš์€ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐ ์ž๊ธˆ์„ ์กฐ๋‹ฌํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@kevinEsherick ๋งž์Šต๋‹ˆ๋‹ค. validateReceiptIos๋ฅผ ์‚ฌ์šฉํ•œ ๋กœ์ปฌ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋Š” ์‹ค์ œ๋กœ ์›๋ž˜์˜ ์ฒซ ๋ฒˆ์งธ ํŠธ๋žœ์žญ์…˜ ID(redux ์ƒํƒœ์— ์ €์žฅ๋จ)๋งŒ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ๋…์— ๋Œ€ํ•œ ์ตœ์‹  ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ ๋‚ด ์„œ๋ฒ„๋ฅผ ์„ค์ •ํ•  ํ•„์š”๊ฐ€ ์—†๊ณ  ๊ตฌ๋…์ด ํ™œ์„ฑํ™”๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๋™์•ˆ iTunes ๋กœ๊ทธ์ธ ํŒ์—…์ด ํ‘œ์‹œ๋˜์ง€ ์•Š๋„๋ก ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@csumrell ๊ณผ ๋‚ด๊ฐ€ ๋ฐฐ์น˜ํ•œ ์ด ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ํ๋ฆ„ ์‚ฌ์šฉ์„ ๊ณ ๋ คํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์„ ์œ„ํ•œ ์ฐธ๊ณ  ์‚ฌํ•ญ: ๊ฐœ๋ฐœ ์ค‘์ธ iTunes ๋กœ๊ทธ์ธ ํ”„๋กฌํ”„ํŠธ๊ฐ€ ์ฒ˜์Œ ์‹œ์ž‘ํ•  ๋•Œ ์—ฌ์ „ํžˆ ํŒ์—…๋˜๋Š” ๊ฒƒ์œผ๋กœ ๋‚˜ํƒ€๋‚ฌ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ํ”„๋กœ๋•์…˜์—์„œ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์ œ์‹œํ•œ ๋‹จ๊ณ„๋ฅผ ๋”ฐ๋ฅด๊ธฐ๋งŒ ํ•˜๋ฉด ๋ชจ๋‘ ๊ดœ์ฐฎ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์ด์œ ์— ๊ด€ํ•ด์„œ๋Š” ์•„๋งˆ๋„ iOS๊ฐ€ IAP๊ฐ€ ์‚ฌ์šฉ ์ค‘์ด๊ฑฐ๋‚˜ ๊ฐœ๋ฐœ ์ค‘์ธ ๋ชจ๋“ˆ ๋งต์˜ ์ผ๋ถ€์ž„์„ ํ™•์ธํ•˜๋ฉด ์ž๋™์œผ๋กœ ๋กœ๊ทธ์ธํ•˜๋ผ๋Š” ๋ฉ”์‹œ์ง€๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. ํ™•์‹คํ•˜์ง€ ์•Š์ง€๋งŒ ์ƒŒ๋“œ๋ฐ•์Šค ํ…Œ์ŠคํŠธ์˜ ๊ธฐ๋Šฅ์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์•ˆ๋…•ํ•˜์„ธ์š”, ์ตœ๊ทผ์— ์ด ๋ฌธ์ œ์— ๋Œ€ํ•œ ํ™œ๋™์ด ์—†๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋˜์—ˆ์Šต๋‹ˆ๊นŒ? ์•„๋‹ˆ๋ฉด ์—ฌ์ „ํžˆ ์ปค๋ฎค๋‹ˆํ‹ฐ์˜ ๊ด€์‹ฌ์ด ํ•„์š”ํ•œ๊ฐ€์š”? ๋” ์ด์ƒ ํ™œ๋™์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š์œผ๋ฉด ์ด ๋ฌธ์ œ๊ฐ€ ์ข…๋ฃŒ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ด ๋ฌธ์ œ์— "ํ† ๋ก ์šฉ" ๋˜๋Š” "์ข‹์€ ์ฒซ ๋ฒˆ์งธ ๋ฌธ์ œ"๋ผ๋Š” ๋ ˆ์ด๋ธ”์„ ๋ถ™์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์—ด์–ด ๋‘๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ท€ํ•˜์˜ ๊ธฐ์—ฌ์— ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

์žฅ๊ธฐ๊ฐ„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ์ด ๋ฌธ์ œ๋ฅผ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๊ฐ€ ์ตœ์‹  ๋ฆด๋ฆฌ์Šค์— ์—ฌ์ „ํžˆ ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ ์–ธ์ œ๋“ ์ง€ ์ตœ์‹  ์ •๋ณด๋กœ ์ƒˆ ๋ฌธ์ œ๋ฅผ ์ƒ์„ฑํ•˜์‹ญ์‹œ์˜ค.

์•„๋งˆ๋„ ์ด ์ „์ฒด ํ๋ฆ„์„ ๋ฌธ์„œ์—์„œ ๋ช…ํ™•ํžˆ ํ•ด์•ผ ํ•ฉ๋‹ˆ๊นŒ?

ํŠนํžˆ ์ž๋™ ๊ฐฑ์‹  ๊ตฌ๋…์˜ ๊ฒฝ์šฐ? expires_date_ms ๊ตฌ๋…์ด ์•„์ง ํ™œ์„ฑํ™”๋˜์–ด ์žˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์—ฌ๋ถ€๋Š” ์‹ ๊ทœ ์ด๋ฏผ์ž์—๊ฒŒ ๋งค์šฐ ๋ถˆ๋ถ„๋ช…ํ•ฉ๋‹ˆ๋‹ค.

๋‚˜๋Š” ์ด๊ฒƒ์„ ์œ„ํ•œ ๊ฐ€์žฅ ์ข‹์€ ์žฅ์†Œ๊ฐ€ ๋ณด๋‹ค ์™„์ „ํ•œ ์ž‘์—… ์˜ˆ์ œ์ผ ๊ฒƒ์ด๋ผ๊ณ  ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ๋žŒ๋“ค์ด ์ด๊ฒƒ์— ๊ด€์‹ฌ์„ ๊ฐ€์งˆ๊นŒ์š”? ์‹œ๊ฐ„์ด ๋œ๋‹ค๋ฉด ์ด ์ž‘์—…์„ ํ•ด๋„ ๋ ๊นŒ์š”?

@alexpchin ๋„ค, ํ™•์‹คํžˆ ๊ฐœ๋ฐœ ์‹œ๊ฐ„์ด ๋งŽ์ด ์ ˆ์•ฝ๋˜๊ณ  ์—…์žฅ์ด ๊นจ๋—ํ•ด์ง‘๋‹ˆ๋‹ค:D

๋™์˜ํ•˜๋‹ค! #856์— ๊ด€์‹ฌ์ด ์žˆ๋Š” ์‚ฌ๋žŒ์ด ์žˆ์Šต๋‹ˆ๊นŒ?

์•ˆ๋…•,

@andrewzey ์˜ ๋ฐฉ๋ฒ•์€ ์–ด์ œ ๊ฐ‘์ž๊ธฐ ๋” ์ด์ƒ ๊ตฌ๋…์— ๋Œ€ํ•œ ์˜์ˆ˜์ฆ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•˜์ง€ ์•Š๊ฒŒ ๋  ๋•Œ๊นŒ์ง€ ํ›Œ๋ฅญํ•˜๊ฒŒ ์ž‘๋™ํ–ˆ์Šต๋‹ˆ๋‹ค. validateReceiptIos()๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ iOS์—์„œ JSON ๊ตฌ๋ฌธ ๋ถ„์„ ์˜ค๋ฅ˜๊ฐ€ ๋‹ค์‹œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ด๊ฒƒ์„ ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ๋งŒ๋‚œ ์ ์ด ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค ...? ์–ด์ œ ์ดํ›„ ๋ณ€๊ฒฝ๋œ ์œ ์ผํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ ๋ณ€์ˆ˜๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜๋„๋ก ์ œ๊ฑฐํ•œ ITC์— ํ”„๋กœ๋ชจ์…˜ ์ฝ”๋“œ๊ฐ€ ์ถ”๊ฐ€๋œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜์ˆ˜์ฆ์ด JSON ๊ตฌ๋ฌธ ๋ถ„์„์— ์‹คํŒจํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ? sortedAvailablePurchases์˜ ์ธ๋ฑ์Šค 0์— ์žˆ๋Š” ์˜์ˆ˜์ฆ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ฐ˜ํ™˜๋œ ๋ชจ๋“  ์˜์ˆ˜์ฆ์— ๋Œ€ํ•ด ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. ์ฝ”๋“œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ andrewzey์˜ ์˜ˆ์™€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— validateReceiptIos ์ดํ›„์— ๋ชจ๋“  ๊ฒƒ์„ ์ƒ๋žตํ–ˆ์Šต๋‹ˆ๋‹ค.

RN 0.61.4, RNIap: 4.4.2 ๋ฐ iOS 13.3.1์„ ์‹คํ–‰ ์ค‘์ž…๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ์‹œ๋„ํ–ˆ์Šต๋‹ˆ๋‹ค:

  • ์•ฑ ์žฌ์„ค์น˜
  • ์ƒˆ ์‚ฌ์šฉ์ž ๊ณ„์ •
  • ์ƒˆ ์•ฑ ์•”ํ˜ธ ์‚ฌ์šฉ
  • ๊ฐ ์‚ฌ์šฉ์ž์˜ ๋ชจ๋“  ์˜์ˆ˜์ฆ ํ…Œ์ŠคํŠธ - ๋‹จ ํ•˜๋‚˜์˜ ์˜์ˆ˜์ฆ๋„ ๊ตฌ๋ฌธ ๋ถ„์„ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์ƒŒ๋“œ๋ฐ•์Šค ๊ตฌ๋งค๋ฅผ ํ•  ๋•Œ finishTransactionIOS๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜๋Š”์ง€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค.

์ •๋ง ์ด์ƒํ•œ ์ ์€ ์ด ์˜จ๋ผ์ธ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์˜์ˆ˜์ฆ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•  ๋•Œ ๋ชจ๋“  ๊ฒƒ์ด ์ •์ƒ์ ์œผ๋กœ ๋ณด์ด๊ณ  ๋ชจ๋“  ์˜์ˆ˜์ฆ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

  isSubscriptionActive = async () => {
      const availablePurchases = await RNIap.getAvailablePurchases();
      const sortedAvailablePurchases = availablePurchases.sort(
        (a, b) => b.transactionDate - a.transactionDate
      );
      const latestAvailableReceipt = sortedAvailablePurchases[0].transactionReceipt;
      const isTestEnvironment = __DEV__;

      try {
        const decodedReceipt = await RNIap.validateReceiptIos(
          {
            'receipt-data': latestAvailableReceipt,
            password: Config.IN_APP_PURCHASE_SECRET,
          },
          isTestEnvironment
        );
        console.log('response!', decodedReceipt)
      } catch (error) {
        console.warn('Error validating receipt', error) // JSON PARSE ERROR HERE
      }
...

@andrewzey๊ฐ€ ๋ฐฐ์น˜ํ•œ ์ฝ”๋“œ์— ๊ด€ํ•œ ์งˆ๋ฌธ์ž…๋‹ˆ๋‹ค. Date.now() ๊ฐ€ ํ˜„์žฌ ์žฅ์น˜ ์‹œ๊ฐ„์ด ์•„๋‹ˆ๊ณ  ์‚ฌ์šฉ์ž๊ฐ€ ์ด๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ  ๋ฌด์ œํ•œ ๊ตฌ๋…์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ๐Ÿค”

๋˜ํ•œ ์•ฑ ์ž์ฒด์—์„œ ์˜์ˆ˜์ฆ์„ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ด ์ข‹์ง€ ์•Š์Šต๋‹ˆ๊นŒ?

@doteric ๊ทธ๋“ค์ด ๋‚™๋‹ดํ–ˆ๋‹ค๊ณ  ์–ธ๊ธ‰ํ•œ ๊ณณ์„ ์•Œ๋ ค ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ? ์„œ๋ฒ„ ์ธก ์•Œ๋ฆผ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์ง€๋งŒ ์ œ ๊ด€์ ์—์„œ๋Š” ์„œ๋ฒ„ ๋Œ€์‹  ํด๋ผ์ด์–ธํŠธ์—์„œ ์ด ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ํ›จ์”ฌ ์‰ฌ์›Œ ๋ณด์ž…๋‹ˆ๋‹ค.
Apple ๋˜๋Š” ๊ธฐํƒ€ ๊ฒฌ๊ณ ํ•œ ๋ฐ˜์‘ ๋„ค์ดํ‹ฐ๋ธŒ ์†Œ์Šค์—์„œ ์˜ฌ๋ฐ”๋ฅธ ๋ฌธ์„œ๋ฅผ ์ฐพ๊ธฐ ์œ„ํ•ด ๊ณ ๊ตฐ๋ถ„ํˆฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

@doteric yes Date.now()๋Š” ์žฅ์น˜ ์‹œ๊ฐ„์ด๋ฏ€๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์‚ฌ์šฉ์ž๊ฐ€ ์ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ๊ฐ€๋Šฅ์„ฑ์€ ํฌ๋ฐ•ํ•˜๋ฉฐ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•ด๋„ ๋์—†๋Š” ๊ตฌ๋…์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๊ฐ„๋‹จํ•œ ์„œ๋ฒ„ ์ธก ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์•ฑ์„ ์—ด๊ธฐ ์ „์— ๋น„ํ–‰๊ธฐ ๋ชจ๋“œ๋กœ ์ „ํ™˜ํ•˜์—ฌ ์•ฑ์ด ๊ตฌ๋…์ด ๋งŒ๋ฃŒ๋˜์—ˆ์Œ์„ ์ธ์‹ํ•˜์ง€ ๋ชปํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌผ๋ก  ๋ฐฐ์น˜ํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค๋ฅธ ๋ณดํ˜ธ ๊ธฐ๋Šฅ์ด ์žˆ์ง€๋งŒ ์ œ ์š”์ ์€ Date.now()๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ ์ธก์ด ํ™•์‹คํžˆ ์ž‘๋™ํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. @captaincole ๋‚˜๋Š” ๋ฌธ์„œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š์ง€๋งŒ ํด๋ผ์ด์–ธํŠธ ์ธก ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ ๊ถŒ์žฅ๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์„ ์ฝ์—ˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๊ทธ๊ฒƒ์„ ๋‚ด๊ฐ€ ๋ฏฟ๋Š” Apple ๋ฌธ์„œ์—์„œ ์ฝ์—ˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰, ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ ์ž‘์—…์„ ์™„๋ฃŒํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

์•ˆ๋…•,

validateReceiptIos ๋ฐ latest_receipt_data(expires_date_ms๋ฅผ ์‹ค์ œ ๋‚ ์งœ์™€ ๋น„๊ต)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด ์Šค๋ ˆ๋“œ์—์„œ ๋…ผ์˜๋œ ์ ‘๊ทผ ๋ฐฉ์‹ ์ค‘ ํ•˜๋‚˜๋ฅผ ์‹œ๋„ํ–ˆ์Šต๋‹ˆ๋‹ค. Xcode์—์„œ ๊ฐœ๋ฐœํ•˜๋Š” ๋™์•ˆ ํ›Œ๋ฅญํ•˜๊ฒŒ ์ž‘๋™ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ Testflight์—์„œ ํ…Œ์ŠคํŠธํ–ˆ์„ ๋•Œ ๋” ์ด์ƒ ์ž‘๋™ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋””๋ฒ„๊น…์ด ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์— ์ •ํ™•ํ•œ ๋ฌธ์ œ๋ฅผ ์‹๋ณ„ํ•  ์ˆ˜ ์—†์œผ๋ฉฐ ์˜์ˆ˜์ฆ ๋ฐ์ดํ„ฐ๋ฅผ ์–ป์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ˆ„๊ตฌ๋“ ์ง€ testflight์™€ ๋น„์Šทํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

๋ฏธ๋ฆฌ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค!

์•ˆ๋…•,

validateReceiptIos ๋ฐ latest_receipt_data(expires_date_ms๋ฅผ ์‹ค์ œ ๋‚ ์งœ์™€ ๋น„๊ต)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด ์Šค๋ ˆ๋“œ์—์„œ ๋…ผ์˜๋œ ์ ‘๊ทผ ๋ฐฉ์‹ ์ค‘ ํ•˜๋‚˜๋ฅผ ์‹œ๋„ํ–ˆ์Šต๋‹ˆ๋‹ค. Xcode์—์„œ ๊ฐœ๋ฐœํ•˜๋Š” ๋™์•ˆ ํ›Œ๋ฅญํ•˜๊ฒŒ ์ž‘๋™ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ Testflight์—์„œ ํ…Œ์ŠคํŠธํ–ˆ์„ ๋•Œ ๋” ์ด์ƒ ์ž‘๋™ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋””๋ฒ„๊น…์ด ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์— ์ •ํ™•ํ•œ ๋ฌธ์ œ๋ฅผ ์‹๋ณ„ํ•  ์ˆ˜ ์—†์œผ๋ฉฐ ์˜์ˆ˜์ฆ ๋ฐ์ดํ„ฐ๋ฅผ ์–ป์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋ˆ„๊ตฌ๋“ ์ง€ testflight์™€ ๋น„์Šทํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

๋ฏธ๋ฆฌ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค!

+1

@asobralr @Somnus007 ์ œ๊ฐ€ ํ‹€๋ฆด ์ˆ˜๋„ ์žˆ์ง€๋งŒ ์‚ฌ์šฉ์ž๊ฐ€ testflight๋ฅผ ํ†ตํ•ด ๊ตฌ๋งคํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— testflight๋กœ IAP๋ฅผ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. Apple์€ ํ”„๋กœ๋•์…˜๊ณผ ์œ ์‚ฌํ•œ ํ™˜๊ฒฝ์—์„œ ๊ตฌ๋งค๋ฅผ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋Š” ์ข‹์€ ๊ธฐํšŒ๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@asobralr @Somnus007 ์ œ๊ฐ€ ํ‹€๋ฆด ์ˆ˜๋„ ์žˆ์ง€๋งŒ ์‚ฌ์šฉ์ž๊ฐ€ testflight๋ฅผ ํ†ตํ•ด ๊ตฌ๋งคํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— testflight๋กœ IAP๋ฅผ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. Apple์€ ํ”„๋กœ๋•์…˜๊ณผ ์œ ์‚ฌํ•œ ํ™˜๊ฒฝ์—์„œ ๊ตฌ๋งค๋ฅผ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋Š” ์ข‹์€ ๊ธฐํšŒ๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์˜ณ์€. ์ƒŒ๋“œ๋ฐ•์Šค์—์„œ ์‹คํŒจ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•  ์ˆ˜๋„ ์—†์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋ถ€๋„๋Ÿฌ์šด ์ผ์ž…๋‹ˆ๋‹ค. ๐Ÿ˜ž

์•ˆ๋…•ํ•˜์„ธ์š” @kevinEsherick๋‹˜ , ๋‹ต๋ณ€ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๋‹น์‹ ์ด ํ‹€๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋Š” teslflight๋ฅผ ํ†ตํ•ด ๊ตฌ๋งคํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. testflight์—์„œ ์‚ฌ์šฉ์ž๋Š” ์‹ค์ œ ์‚ฌ๊ณผ ๊ณ„์ •๊ณผ ๋™์ผํ•œ ์ด๋ฉ”์ผ ์ฃผ์†Œ๋ฅผ ๊ฐ€์ง„ ์ƒŒ๋“œ๋ฐ•์Šค ๊ณ„์ •์— ๋กœ๊ทธ์ธํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๋ชจ๋“  ๊ฒƒ์ด ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. BTW, getPurchaseHistory ๊ฐ€ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ iTunes ๋กœ๊ทธ์ธ ํŒ์—…์„ ํŠธ๋ฆฌ๊ฑฐํ•ฉ๋‹ˆ๊นŒ?

ios 14๋Š” requestSubscription()์„ ํ˜ธ์ถœํ•˜๊ธฐ ์ „์— getAvailablePurchases()๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค๋Š” ์‚ฌ์‹ค ๋•Œ๋ฌธ์— ์ด ์†”๋ฃจ์…˜์„ ์ค‘๋‹จํ•œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰