모바일 웹뷰 인앱결제 에러

이 글의 성격은 무엇인가요?

질문 / 문제 해결

내용을 설명해주세요

안녕하세요. 고생 많으십니다

이번에 리액트 18버전에서 인앱결제를 붙이고 있습니다.

위 문서를 참고하여 구현을 하고 있는데, createOneTimePurchaseOrder에서 오류가 발생합니다.

function handleBuyProduct(sku) {
  cleanup = IAP.createOneTimePurchaseOrder({
    options: {
      sku,
      processProductGrant: async () => {
        // 여기로 진입하기는 합니다만,
        const success = await grantProduct(
          // ...
        );
        // 여기까지 못 옵니다. 분명히 grantProduct의 문제는 아닌 것을 확인했습니다.
        return true;
      },
    },
    onEvent: (event) => {
      console.log('이벤트:', event);
      cleanup?.();
    },
    onError: (error) => {
      console.error('인앱결제에 실패했어요:', error);
      cleanup?.();
    },
  });
}

위 예제에서 processProductGrant 메소드 내 GCR을 통해 상품을 지급하는 행위를 하고 있습니다만,
SDK 단에서 작업을 점유하고 있는 것 같습니다.

결제 성공 후 30초내에 processProductGrant 콜백이 호출되지 않거나 해당 콜백의 결과가 true가 아닌 경우,
{appName}에 문제가 생겼어요. 환불을 신청해주세요 페이지가 노출될 수 있어요.

계속 위와 같은 현상이 발생합니다.

안에 있는 grantProduct의 응답이 계속해서 오지 않는데, 혹시 제가 잘못하고 있는 부분이 있을까요?
다른 분들은 모바일 웹뷰로 도대체 어떻게 구현하신 건지 궁금하네요.

안녕하세요 :slight_smile:
processProductGrant(){ orderId }를 넣어서 호출하도록 해주실 수 있나요 ?

function IapCreateOneTimePurchaseOrderButton({ sku }: Props) { 
  const handleBuy = useCallback(() => {
    const cleanup = IAP.createOneTimePurchaseOrder({
      options: {
        sku,
        processProductGrant: ({ orderId }) => {
          // 상품 지급 로직을 작성해요.
          return true; // 상품 지급 여부를 반환해요.
        }
      },
      onEvent: (event) => {
        console.log(event);
        cleanup();
      },
      onError: (error) => {
        console.error(error);
        cleanup();
      },
    });
  }, [sku]);

  return <Button onClick={handleBuy}>구매하기</Button>;
}

답변 달아주셔서 감사합니다!
위 예제에서는 생략한 것일 뿐, 이미 넣어서 호출하고 있었습니다.

        cleanup = IAP.createOneTimePurchaseOrder({
          options: {
            sku: params.sku,
            processProductGrant: async ({ orderId }: { orderId: string }) => {
              try {
                // 여기로 진입하기는 합니다만,
                const success = await grantProduct( // 30초 동안 호출이 되지 않습니다.
                  orderId,
                  params.sku,
                  subjectId,
                );
                // 여기까지 못 옵니다. 분명히 grantProduct의 문제는 아닌 것을 확인했습니다.
                return success;
              } catch {}
            },
          },
          onEvent: async (event: { type: string; data?: unknown }) => {
            // ...
          },
          onError: async (error: unknown) => {
            // ...
          },

엇, 죄송하지만 grantProduct()는 어떤 예제를 참고하셨을까요 ?

grantProduct는 예제가 없지 않나요?

// 상품 지급 로직을 작성해요.
// 상품 지급 여부를 반환해요.

위 부분에 해당하는 코드입니다!

export async function grantProduct(
  orderId: string,
  sku: string,
): Promise<boolean> {
  try {
    const response = await confirmSubscriptionViaGcr({
      // 30초 동안 응답이 막힙니다. 그리고 '환불...'로 이동합니다.
      orderId,
      tossProductId: sku,
    });

    if (response.success) {
      return true;
    }

    if (response.error.code === "ORDER_ALREADY_CONSUMED") {
      return true;
    }
  } catch (error) {
    return false;
  }
}

좀 더 정확하게 설명드리면,

위 사진처럼 뜨고, GCR에서 내부 동작이 정상적으로 되지만 응답은 막힙니다.
그리고 “{appName}에 문제가 생겼어요. 환불을 신청해주세요 페이지가 노출될 수 있어요.”로 이동합니다.
혹시 CORS 등, 기타 문제로 SDK 단에서 막는게 있나요?

아니면 혹시 저희가 아래 플로우의 '상품 지급 처리’에서

GCR 내부에서 유효성 검증을 위해
POST /api-partner/v1/apps-in-toss/order/get-order-status를 호출하고 있는데,
이게 문제일까요?
(processProductGrant가 다 끝나고 호출해야 한다던지, 그러나 아래 설명을 보면 이게 아닌듯 합니다만
개발하기 | 앱인토스 개발자센터 )

그리고 저희는 현재 토스 로그인을 사용하고 있습니다.

        cleanup = IAP.createOneTimePurchaseOrder({
          options: {
            sku: params.sku,
            processProductGrant: async ({ orderId }: { orderId: string }) => {
              try {
                const success = await grantProduct( // 응답을 받을 수 없어 30초가 지나가버림
                  orderId,
                  params.sku,
                  subjectId,
                );
                return success;
              } catch {}
            },
          },
          onEvent: async (event: { type: string; data?: unknown }) => {
            // ...
          },
          onError: async (error: unknown) => {
            // ...
          },

여기서 만약 우회하기 위해 processProductGrant에서 그냥 return true하고 onEvent에서 grantProduct를 호출하면 잘 작동하는데, processProductGrant 안에서는 계속해서 응답을 못받습니다.

자체 서버를 호출하시는 구간인거죠 ?!
CORS 허용은 해주셨을까요 ?

예. GCR로 구현된 자체 서버를 호출하는 구간이 맞습니다.
CORS 등 모든 부분을 확인하였으나, 분명히 서버에서는 올바르게 응답을 반환하고 있습니다.
SDK 단에서 문제가 발생하는 것으로 보입니다.
정확히 말씀드리면 30초간 SDK 단에서 GCR 호출 또는 응답을 막고 있습니다.
결제 때문에 일주일 넘게 출시를 못하고 있는데, 진짜 미치겠네요 ㅠㅠ

혹시 grantProduct 호출 브라우저 네트워크탭에서는 어떻게 보이나요 ?!
그냥 30초 후 타임아웃인지요 ?!