디지털 구독 트랜잭션 빌드 (Dialogflow)

이 가이드에서는 사용자가 정기 결제를 구매할 수 있도록 대화형 작업에 디지털 정기 결제 트랜잭션을 추가하는 방법을 설명합니다.

핵심 용어: 정기 결제 디지털 상품은 온라인 잡지와 같이 사용자에게 반복 청구를 요구하는 재고 관리 단위 (SKU)입니다. 이는 사용자가 수동으로 다시 구매해야 하는 소비성 디지털 상품이나 자동으로 한 번만 구매되는 비소비성 디지털 상품과는 다릅니다.

디지털 정기 결제에 관한 자세한 내용은 정기 결제 관련 기능에 관한 Android 문서를 참고하세요.

제한사항 및 검토 가이드라인

거래가 있는 작업에는 추가 정책이 적용됩니다. 트랜잭션이 포함된 작업을 검토하는 데 몇 주가 걸릴 수 있으므로 출시 일정을 계획할 때 이 시간을 고려하세요. 검토 프로세스를 쉽게 하려면 검토를 위해 작업을 제출하기 전에 트랜잭션 정책 및 가이드라인을 준수해야 합니다.

디지털 상품을 판매하는 작업은 다음 국가에서만 배포할 수 있습니다.

  • 오스트레일리아
  • 브라질
  • 캐나다
  • 인도네시아
  • 일본
  • 멕시코
  • 러시아
  • 싱가포르
  • 태국
  • 터키
  • 영국
  • 미국

거래 흐름

이 가이드에서는 디지털 상품 거래 흐름에서 발생하는 각 개발 단계를 간략히 설명합니다. 작업이 디지털 상품 거래를 처리할 때 다음 흐름을 사용합니다.

  1. 디지털 구매 API 클라이언트 설정: 작업이 디지털 구매 API를 사용하여 Google Play 인벤토리와 통신하고 거래합니다. 작업이 다른 작업을 수행하기 전에 디지털 구매 API와 통신하는 서비스 키를 사용하여 JWT 클라이언트를 만듭니다.
  2. 정보 수집: 작업이 사용자와 Google Play 인벤토리에 관한 기본 정보를 수집하여 거래를 준비합니다.
    1. 거래 요구사항 확인: 작업이 구매 흐름을 시작할 때 디지털 거래 요구사항 도우미를 사용하여 사용자가 거래할 수 있는지 확인합니다.
    2. 사용 가능한 인벤토리 수집: 작업이 Google Play 인벤토리를 확인하고 현재 구매할 수 있는 항목을 식별합니다.
  3. 주문 빌드: 작업은 사용자가 구매할 디지털 상품을 선택할 수 있도록 사용 가능한 디지털 상품을 제공합니다.
  4. 구매 완료: 작업이 디지털 구매 API를 사용하여 사용자가 선택한 항목으로 Google Play 스토어에서 구매를 시작합니다.
  5. 결과 처리: 작업이 거래의 상태 코드를 수신하고 사용자에게 구매가 완료되었음을 알립니다 (또는 추가 단계를 실행함).

기본 요건

디지털 트랜잭션을 작업에 통합하려면 먼저 다음과 같은 기본 요건을 충족해야 합니다.

  • Google Play의 개발자 계정판매자 계정: Google Play Console에서 디지털 상품을 관리합니다.

  • Google Search Console에서 확인된 웹 도메인 이 도메인은 공개적으로 출시된 웹사이트와 연결될 필요는 없으며, 단순히 웹 도메인을 참조하기만 하면 됩니다.

  • Google Play Console에서 com.android.vending.BILLING 권한이 있는 Android 앱 디지털 상품은 Google Play Console에서 이 앱과 연결된 '인앱 구매'가 됩니다.

    이 앱을 사용하여 Play Console에서도 버전을 만들어야 하지만, 버전을 공개하지 않으려면 비공개 알파 출시를 만들면 됩니다.

    아직 Android 앱이 없다면 Android 앱 연결 안내를 따르세요.

  • 작업을 통해 판매하는 디지털 상품인 Google Play Console에 있는 하나 이상의 정기 결제 Android 앱 기본 요건을 설정할 때까지 Play Console에서 정기 결제를 만들 수 없습니다.

    아직 정기 결제 항목이 없다면 디지털 상품 만들기 안내를 따릅니다.

Android 앱 연결

현재 Google Play Console에 결제 권한이 있는 Android 앱이 없다면 다음 단계를 따르세요.

  1. Android 스튜디오 또는 원하는 Android IDE에서 새 프로젝트를 만듭니다. 프로젝트 설정 메시지에서 옵션을 선택하여 매우 기본적인 앱을 만듭니다.
  2. 프로젝트에 패키지 이름을 지정합니다(예: com.mycompany.myapp). com.example가 포함된 패키지는 Play Console에 업로드할 수 없으므로 이 이름을 기본값으로 두지 마세요.
  3. 앱의 AndroidManifest.xml 파일을 엽니다.
  4. manifest 요소 내에 다음 코드 줄을 추가합니다.

    <uses-permission android:name="com.android.vending.BILLING" />

    AndroidManifest.xml 파일은 다음 코드 블록과 같이 표시됩니다.

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        package="com.mycompany.myapp">
        <uses-permission android:name="com.android.vending.BILLING" />
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme" />
    </manifest>
    
  5. 앱을 서명된 APK로 빌드합니다. Android 스튜디오에서 다음 단계를 따르세요.

    1. Build, Generate Signed Bundle / APK로 이동합니다.
    2. 다음을 클릭합니다.
    3. Key store path 아래에서 Create new를 클릭합니다.
    4. 각 입력란을 작성한 후 OK를 클릭합니다. 키 저장소 비밀번호키 비밀번호를 기록해 두고 나중에 사용할 수 있도록 안전한 장소에 보관합니다.
    5. 다음을 클릭합니다.
    6. 출시를 선택합니다.
    7. V1 (JAR Signature)(V1(JAR 서명))을 선택합니다.
    8. Finish를 클릭합니다.
    9. 몇 초 후에 Android 스튜디오에서 app-release.apk 파일을 생성합니다. 나중에 사용할 수 있도록 이 파일을 찾습니다.
  6. Google Play Console에서 새 애플리케이션을 만듭니다.

  7. 앱 버전으로 이동합니다.

  8. 비공개 트랙에서 관리로 이동한 다음 알파로 이동합니다.

  9. 새 버전 출시하기 버튼을 클릭합니다.

  10. Google에서 서명 키를 관리하고 보호하도록 허용에서 서명 키 정보를 입력합니다.

  11. APK 파일을 업로드합니다.

  12. 저장을 클릭합니다.

디지털 상품 만들기

현재 Play Console에 디지털 상품이 없다면 다음 단계를 따르세요.

  1. Google Play Console에서 인앱 상품으로 이동한 다음 정기 결제로 이동합니다. 경고가 표시되면 이전 안내에 따라 Android 앱을 만들거나 링크를 클릭하여 판매자 프로필을 만듭니다.
  2. 구독 만들기를 클릭합니다.
  3. 디지털 제품 입력란을 작성합니다. 작업에서 이 제품을 참조하는 방법인 제품 ID를 기록해 둡니다.
  4. 저장을 클릭합니다.
  5. 판매하려는 각 제품에 대해 2~4단계를 반복합니다.

Google Play Console의 정기 결제 예

작업 프로젝트 준비

Google Play Console에서 디지털 상품을 설정했으면 디지털 거래를 사용 설정하고 작업 프로젝트를 Play 앱과 연결해야 합니다.

작업 프로젝트에서 디지털 상품 거래를 사용 설정하려면 다음 단계를 따르세요.

  1. Actions 콘솔에서 프로젝트를 열거나 새 프로젝트를 만듭니다.
  2. 배포로 이동한 다음 디렉터리 정보로 이동합니다.
  3. 추가 정보거래에서 작업에서 Digital Purchase API를 사용하여 디지털 상품 거래를 실행하나요? 아래의 체크박스를 선택합니다.
  4. 저장을 클릭합니다.

디지털 상품 API 키 만들기

디지털 상품 API에 요청을 보내려면 Actions 콘솔 프로젝트와 연결된 JSON 서비스 계정 키를 다운로드해야 합니다.

서비스 계정 키를 검색하려면 다음 단계를 따르세요.

  1. Actions 콘솔에서 오른쪽 상단에 있는 점 3개 아이콘을 클릭한 다음 프로젝트 설정을 클릭합니다.
  2. 작업의 프로젝트 ID를 찾습니다.
  3. 다음 링크를 따라 '<project_id>'를 프로젝트 ID로 바꿉니다. https://console.developers.google.com/apis/credentials?project=project_id
  4. 기본 탐색 메뉴에서 사용자 인증 정보로 이동합니다.
  5. 표시되는 페이지에서 사용자 인증 정보 만들기를 클릭한 다음 서비스 계정 키를 클릭합니다.
  6. 서비스 계정으로 이동하고 새 서비스 계정을 클릭합니다.
  7. 서비스 계정에 Digitaltransactions와 같은 이름을 지정합니다.
  8. 만들기를 클릭합니다.
  9. 역할프로젝트 > 소유자로 설정합니다.
  10. 계속을 클릭합니다.
  11. 키 만들기를 클릭합니다.
  12. JSON 키 유형을 선택합니다.
  13. 키 만들기를 클릭하고 JSON 서비스 계정 키를 다운로드합니다.

이 서비스 계정 키를 안전한 장소에 저장하세요. 처리에서 이 키를 사용하여 디지털 구매 API의 클라이언트를 만듭니다.

Play 인벤토리에 연결

작업 프로젝트에서 디지털 상품에 액세스하려면 웹 도메인 및 앱을 프로젝트와 연결된 속성으로 연결합니다.

참고: Google에서 속성을 확인하는 동안 연결 단계가 완료되는 데 최대 일주일이 걸릴 수 있습니다. 이 기간이 지나도 웹사이트나 앱이 연결되지 않으면 지원팀에 문의하세요.

Play Console 웹 도메인 및 앱을 작업 프로젝트에 연결하려면 다음 단계를 따르세요.

  1. Actions 콘솔에서 배포로 이동한 다음 브랜드 인증으로 이동합니다.
  2. 속성을 연결하지 않은 경우 먼저 웹사이트를 연결하세요.

    1. 웹 속성 (</>) 버튼을 클릭합니다.
    2. 웹 도메인의 URL을 입력하고 연결을 클릭합니다.

    Google은 Google Search Console에서 웹 도메인을 확인한 개인에게 추가 안내가 포함된 이메일을 보냅니다. 이 이메일의 수신자가 이러한 단계를 따르면 웹사이트가 브랜드 인증에 표시됩니다.

  3. 연결된 웹사이트가 있으면 다음 단계를 따라 Android 앱을 연결하세요.

    1. Actions 콘솔에서 배포로 이동한 다음 브랜드 인증으로 이동합니다.
    2. 앱 연결을 클릭합니다.
    3. 페이지가 표시되면 안내에 따라 Play Console에서 웹 도메인을 확인합니다. 디지털 상품이 포함된 Play 앱을 선택하고 브랜드 인증 페이지에 표시된 대로 웹 도메인 URL을 정확하게 입력합니다.

      다시 한번 Google이 도메인의 확인된 소유자에게 확인 이메일을 보냅니다. 개발자가 인증을 승인하면 Play 앱이 브랜드 인증 아래에 표시됩니다.

    4. Play 구매 항목 액세스를 사용 설정합니다.

작업 프로젝트에 연결된 웹사이트와 앱을 보여주는 이미지입니다.

구매 흐름 빌드

작업 프로젝트와 디지털 상품 인벤토리가 준비되었으면 대화 처리 웹훅에서 디지털 상품 구매 흐름을 빌드합니다.

1. 디지털 구매 API 클라이언트 설정

대화 처리 웹훅에서 서비스 계정 JSON 키와 https://www.googleapis.com/auth/actions.purchases.digital 범위를 사용하여 JWT 클라이언트를 만듭니다.

다음 Node.js 코드는 디지털 구매 API를 위한 JWT 클라이언트를 만듭니다.

  const serviceAccount = {'my-file.json'};
  const request = require('request');
  const {google} = require('googleapis');

  const jwtClient = new google.auth.JWT(
    serviceAccount.client_email, null, serviceAccount.private_key,
    ['https://www.googleapis.com/auth/actions.purchases.digital'],
    null
  );

2. 정보 수집

사용자가 구매하기 전에 작업은 사용자의 구매 능력과 인벤토리에서 사용할 수 있는 상품에 관한 정보를 수집합니다.

2. a. 거래 요구사항 확인

사용자에게 구매 옵션을 제공하기 전에 사용자 계정이 거래를 실행하도록 설정되어 있는지 확인하는 것이 좋습니다. 이 단계에는 사용자가 결제 수단을 구성했는지, 사용자가 디지털 거래가 지원되는 지역에 있는지 확인하는 작업이 포함됩니다. 트랜잭션 흐름 시작 시 DIGITAL_PURCHASE_CHECK 도우미를 사용하여 어시스턴트를 통한 사용자의 트랜잭션 구성을 검증합니다.

다음 Node.js 코드는 대화 시작 시 DIGITAL_PURCHASE_CHECK를 사용합니다.

app.intent('Default Welcome Intent', async (conv, { SKU }) => {
  // Immediately invoke digital purchase check intent to confirm
  // purchase eligibility.
  conv.ask(new DigitalPurchaseCheck());
});

대화 인수에서 이 검사 결과를 DIGITAL_PURCHASE_CHECK_RESULT로 찾습니다. 이 결과에 따라 거래 흐름을 계속하거나 피벗하여 Google Pay 구성을 확인하라는 메시지를 표시합니다.

다음 Node.js 코드는 요구사항 확인 결과를 처리합니다.

app.intent('Digital Purchase Check', async (conv) => {
  const arg = conv.arguments.get('DIGITAL_PURCHASE_CHECK_RESULT');
  if (!arg || !arg.resultType) {
    conv.close('Digital Purchase check failed. Please check logs.');
    return;
  }
  // User does not meet necessary conditions for completing a digital purchase
  if (arg.resultType === 'CANNOT_PURCHASE' || arg.resultType === 'RESULT_TYPE_UNSPECIFIED') {
    conv.close(`It looks like you aren't able to make digital purchases. Please check your Google Pay configuration and try again.`);
    return;
  }
  conv.ask('Welcome to the Digital Goods Sample. Would you like to see what I have for sale?');
});

2. 사용 가능한 인벤토리 수집

디지털 구매 API를 사용하여 현재 사용 가능한 Play 스토어 인벤토리를 요청한 다음 각 제품의 JSON 객체 배열로 빌드합니다. 나중에 이 배열을 참조하여 사용자에게 구매할 수 있는 옵션을 보여줍니다.

각 디지털 상품은 JSON 형식의 SKU로 표시됩니다. 다음 Node.js 코드는 각 SKU에 예상되는 형식을 간략히 설명합니다.

body = {
  skus: [
    skuId: {
      skuType: one of "APP" or "UNSPECIFIED"
      id: string,
      packageName: string
    }
    formattedPrice: string,
    title: string,
    description: string
  ]
}

https://actions.googleapis.com/v3/packages/{packageName}/skus:batchGet 엔드포인트로 POST 요청을 전송합니다. 여기서 {packageName}는 Google Play Console에 있는 앱의 패키지 이름 (예: com.myapp.digitalgoods)입니다. 결과의 형식을 SKU 객체의 배열로 지정합니다.

결과 배열에서 특정 디지털 상품만 검색하려면 body.ids에서 구매할 수 있도록 하려는 디지털 상품의 제품 ID (Google Play Console의 각 인앱 상품 아래에 표시됨)를 나열합니다.

다음 Node.js 코드는 디지털 구매 API에서 사용 가능한 상품 목록을 요청하고 결과의 형식을 SKU의 배열로 지정합니다.

return jwtClient.authorize((err, tokens) => {
    if (err) {
      throw new Error(`Auth error: ${err}`);
    }

    const packageName = 'com.example.projectname';

    request.post(`https://actions.googleapis.com/v3/packages/${packageName}/skus:batchGet`, {
      'auth': {
        'bearer': tokens.access_token,
      },
      'json': true,
      'body': {
        'conversationId': conversationId,
        'skuType': 'APP',
        // This request is filtered to only retrieve SKUs for the following product IDs
        'ids': ['annual.subscription']
      },
    }, (err, httpResponse, body) => {
      if (err) {
        throw new Error(`API request error: ${err}`);
      }
      console.log(`${httpResponse.statusCode}: ${httpResponse.statusMessage}`);
      console.log(JSON.stringify(body));
    });
  });
});

3. 주문 만들기

사용자의 디지털 구매를 시작하려면 구매할 수 있는 디지털 상품 목록을 제시합니다. 다양한 리치 응답 유형을 사용하여 주식을 나타내고 사용자에게 선택을 요청하는 메시지를 표시할 수 있습니다.

다음 Node.js 코드는 SKU 객체의 인벤토리 배열을 읽고 각각 하나의 목록 항목이 있는 목록 응답을 만듭니다.

skus.forEach((sku) => {
  const key = `${sku.skuId.skuType},${sku.skuId.id}`
  list.items[key] = {
    title: sku.title,
    description: `${sku.description} | ${sku.formattedPrice}`,
  };
});

4. 구매를 완료합니다.

구매를 완료하려면 COMPLETE_PURCHASE 도우미 인텐트를 사용자가 선택한 항목과 함께 사용합니다.

다음 Node.js 코드는 목록 응답에서 사용자의 SKU 선택을 처리하고 이 정보를 사용하여 COMPLETE_PURCHASE 인텐트를 요청합니다.

app.intent('Send Purchase', (conv, params, option) => {
  let [skuType, id] = option.split(',');

  conv.ask(new CompletePurchase({
    skuId: {
      skuType: skuType,
      id: id,
      packageName: <PACKAGE_NAME>,
    },
  }));
});

5. 결과 처리

구매가 완료되면 결과를 설명하는 COMPLETE_PURCHASE_VALUE 인수를 사용하여 actions_intent_COMPLETE_PURCHASE Dialogflow 이벤트 (또는 actions.intent.COMPLETE_PURCHASE Actions SDK 인텐트)를 트리거합니다. 이 이벤트에 의해 트리거되어 사용자에게 결과를 전달하는 인텐트를 빌드합니다.

다음과 같은 가능한 구매 결과를 처리합니다.

  • PURCHASE_STATUS_OK: 구매가 완료되었습니다. 이 시점에서 트랜잭션이 완료되었으므로 트랜잭션 흐름을 종료하고 대화로 다시 돌아갑니다.
  • PURCHASE_STATUS_ALREADY_OWNED: 사용자가 이미 항목을 소유하고 있으므로 거래에 실패했습니다. 사용자의 이전 구매를 확인하고 이미 소유한 항목을 다시 구매할 수 있는 옵션이 없도록 표시되는 항목을 조정하여 이 오류를 방지합니다.
  • PURCHASE_STATUS_ITEM_UNAVAILABLE: 요청된 항목을 사용할 수 없어 트랜잭션이 실패했습니다. 구매 시점에 더 가까운 사용 가능한 SKU를 확인하여 이 오류를 방지하세요.
  • PURCHASE_STATUS_ITEM_CHANGE_REQUESTED: 사용자가 다른 상품을 구매하기로 결정했기 때문에 거래에 실패했습니다. 사용자가 즉시 다른 결정을 내릴 수 있도록 주문 작성으로 다시 메시지를 표시합니다.
  • PURCHASE_STATUS_USER_CANCELLED: 사용자가 구매 흐름을 취소하여 거래에 실패했습니다. 사용자가 흐름을 너무 일찍 종료했으므로 사용자에게 트랜잭션을 재시도할지 아니면 트랜잭션을 완전히 종료할지 묻습니다.
  • PURCHASE_STATUS_ERROR: 알 수 없는 이유로 거래에 실패했습니다. 사용자에게 거래가 실패했다고 알리고 다시 시도할지 묻습니다.
  • PURCHASE_STATUS_UNSPECIFIED: 알 수 없는 이유로 트랜잭션이 실패하여 상태를 알 수 없습니다. 이 오류 상태는 사용자에게 트랜잭션 실패를 알리고 다시 시도할지 물어보는 방식으로 처리합니다.

다음 Node.js 코드는 COMPLETE_PURCHASE_VALUE 인수를 읽고 각 결과를 처리합니다.

app.intent('Purchase Result', (conv) => {
  const arg = conv.arguments.get('COMPLETE_PURCHASE_VALUE');
  console.log('User Decision: ' + JSON.stringify(arg));
  if (!arg || !arg.purchaseStatus) {
    conv.close('Purchase failed. Please check logs.');
    return;
  }
  if (arg.purchaseStatus === 'PURCHASE_STATUS_OK') {
    conv.close(`Purchase completed! You're all set!`);
  } else if (arg.purchaseStatus === 'PURCHASE_STATUS_ALREADY_OWNED') {
    conv.close('Purchase failed. You already own this item.');
  } else if (arg.purchaseStatus === 'PURCHASE_STATUS_ITEM_UNAVAILABLE') {
    conv.close('Purchase failed. Item is not available.');
  } else if (arg.purchaseStatus === 'PURCHASE_STATUS_ITEM_CHANGE_REQUESTED') {
    // Reprompt with your item selection dialog
  }  else {
    conv.close('Purchase Failed:' + arg.purchaseStatus);
  }
});

사용자의 구매 반영

사용자가 작업을 쿼리하면 요청 JSON의 user 객체에 구매 목록이 포함됩니다. 이 정보를 확인하고 사용자가 결제한 콘텐츠에 따라 작업의 응답을 변경하세요.

다음 샘플 코드는 com.digitalgoods.application 패키지와 관련하여 이전 인앱 구매의 packageEntitlements가 포함된 요청의 user 객체를 보여줍니다.

  "user": {
    "userId": "xxxx",
    "locale": "en-US",
    "lastSeen": "2018-02-09T01:49:23Z",
    "packageEntitlements": [
      {
        "packageName": "com.digitalgoods.application",
        "entitlements": [
          {
            "sku": "non-consumable.1",
            "skuType": "APP"
          }
          {
            "sku": "consumable.2",
            "skuType": "APP"
          }
        ]
      },
      {
        "packageName": "com.digitalgoods.application",
        "entitlements": [
          {
            "sku": "annual.subscription",
            "skuType": "SUBSCRIPTION",
            "inAppDetails": {
              "inAppPurchaseData": {
                "autoRenewing": true,
                "purchaseState": 0,
                "productId": "annual.subscription",
                "purchaseToken": "12345",
                "developerPayload": "HSUSER_IW82",
                "packageName": "com.digitalgoods.application",
                "orderId": "GPA.233.2.32.3300783",
                "purchaseTime": 1517385876421
              },
              "inAppDataSignature": "V+Q=="
            }
          }
        ]
      }
    ]
  },
  "conversation": {
    "conversationId": "1518141160297",
    "type": "NEW"
  },
  "inputs": [
    {
      "intent": "actions.intent.MAIN",
      "rawInputs": [
        {
          "inputType": "VOICE",
          "query": "Talk to My Test App"
        }
      ]
    }
  ],
  ...
}