Google Pay로 실제 거래 구축하기

이 가이드에서는 실제 상품의 트랜잭션을 통합하고 결제에 Google Pay를 사용하는 작업 프로젝트를 개발하는 과정을 안내합니다.

거래 흐름

작업 프로젝트는 판매자 관리 결제를 사용하여 실제 거래를 처리할 때 다음 흐름을 사용합니다.

  1. 정보 수집 (선택사항) - 트랜잭션의 특성에 따라 대화 시작 시 사용자로부터 다음 정보를 수집할 수 있습니다.
    1. 거래 요구사항 유효성 검사 - 대화를 시작할 때 장바구니를 만들기 전에 결제 정보를 올바르게 구성하고 사용할 수 있는지와 같은 거래 요구사항을 충족하는지 확인합니다.
    2. 배송 주소 요청 - 거래에 배송지 주소가 필요한 경우 사용자로부터 주소를 수집합니다.
  2. 주문 작성 - 사용자가 구매할 상품을 선택하는 '장바구니 조립' 과정을 안내합니다.
  3. 주문 제안 - 장바구니가 완료되면 사용자에게 주문을 제안하여 올바른지 확인할 수 있도록 합니다. 주문이 확인되면 주문 세부정보와 결제 토큰이 포함된 응답을 받게 됩니다.
  4. 주문 완료 및 영수증 전송 - 주문을 확인한 후 재고 추적 또는 기타 처리 서비스를 업데이트한 다음 사용자에게 영수증을 보냅니다.
  5. 주문 업데이트 전송 - 주문 처리 수명 기간 동안 PATCH 요청을 Orders API에 전송하여 사용자에게 주문 업데이트를 제공합니다.

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

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

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

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

프로젝트 빌드

트랜잭션 대화의 광범위한 예는 Node.js 트랜잭션 샘플을 참조하세요.

설정

작업을 만들 때 Actions 콘솔에서 트랜잭션을 실행하도록 지정해야 합니다.

프로젝트와 처리를 설정하려면 다음을 실행하세요.

  1. 새 프로젝트를 만들거나 기존 프로젝트를 가져옵니다.
  2. 배포 > 디렉터리 정보로 이동합니다.
  3. 추가 정보 > 거래에서 '작업에서 실제 상품 거래를 실행하기 위해 거래 API를 사용하나요?'라는 체크박스를 선택합니다.

1. 정보 수집(선택사항)

1a. 거래 요구사항 확인(선택사항)

사용자가 구매 의사를 밝힌 즉시 사용자가 거래를 할 수 있는지 확인해야 합니다. 예를 들어 호출되면 작업이 '신발을 주문하시겠어요? 아니면 계좌 잔액을 확인하시겠어요?'라고 물을 수 있습니다. 사용자가 '신발 주문'이라고 말하면 계속 진행할 수 있는지 확인하고 거래를 계속할 수 없도록 하는 설정을 수정할 기회를 제공해야 합니다. 이렇게 하려면 트랜잭션 요구사항 확인을 실행하는 장면으로 전환해야 합니다.

거래 요구사항 확인 장면 만들기
  1. Scenes 탭에서 이름이 TransactionRequirementsCheck인 새 장면을 추가합니다.
  2. 슬롯 채우기에서 +를 클릭하여 새 슬롯을 추가합니다.
  3. 유형 선택에서 슬롯 유형으로 actions.type.TransactionRequirementsCheckResult를 선택합니다.
  4. 슬롯 이름 필드에서 슬롯 이름을 TransactionRequirementsCheck로 지정합니다.
  5. 슬롯 값 쓰기백 맞춤설정 체크박스를 사용 설정합니다 (기본적으로 사용 설정됨).
  6. 저장을 클릭합니다.

거래 요구사항을 확인하면 다음 결과 중 하나가 발생합니다.

  • 요구사항이 충족되면 세션 매개변수가 성공 조건과 함께 설정되며, 사용자의 주문 작성을 진행할 수 있습니다.
  • 요구사항을 하나 이상 충족할 수 없으면 세션 매개변수가 실패 조건으로 설정됩니다. 이 경우 대화를 거래 환경에서 전환하거나 대화를 종료해야 합니다.
    • 사용자가 실패 상태로 이어지는 오류를 수정할 수 있는 경우 기기에서 문제를 해결하라는 메시지가 표시됩니다. 음성 전용 표면에서 대화가 이루어지는 경우 사용자의 휴대전화로 핸드오프가 시작됩니다.

거래 요구사항 확인 결과 처리

  1. Scenes(장면) 탭에서 새로 만든 TransactionRequirementsCheck 장면을 선택합니다.
  2. 조건에서 +를 클릭하여 새 조건을 추가합니다.
  3. 텍스트 필드에 다음 조건 구문을 입력하여 성공 조건을 확인합니다.

    scene.slots.status == "FINAL" && session.params.TransactionRequirementsCheck.resultType == "CAN_TRANSACT"
    
  4. 방금 추가한 조건 위로 커서를 가져간 후 위쪽 화살표를 클릭하여 if scene.slots.status == "FINAL" 앞에 배치합니다.

  5. 메시지 보내기를 사용 설정하고 사용자에게 트랜잭션 준비가 되었음을 알리는 간단한 메시지를 제공합니다.

    candidates:
      - first_simple:
          variants:
            - speech: >-
                You are ready to purchase physical goods.
    
  6. Transition에서 다른 장면을 선택하여 사용자가 대화를 계속하고 트랜잭션을 진행할 수 있도록 합니다.

  7. 조건 else if scene.slots.status == "FINAL"을 선택합니다.

  8. 메시지 전송을 사용 설정하고 사용자에게 거래를 할 수 없음을 알리는 간단한 메시지를 제공합니다.

    candidates:
      - first_simple:
          variants:
            - speech: Transaction requirements check failed.
    
  9. 사용자가 거래를 할 수 없는 경우 Transition에서 EndConversation을 선택하여 대화를 종료합니다.

배송지 주소 요청

거래에 사용자의 배송지 주소가 필요한 경우 사용자에게 요청해야 합니다. 총 가격, 배달/수령 위치를 확인하거나 사용자가 서비스 지역 내에 있는지 확인하는 데 유용할 수 있습니다. 이렇게 하려면 사용자에게 배송 주소를 묻는 장면으로 전환해야 합니다.

배송 주소 장면 만들기

  1. Scenes 탭에서 이름이 DeliveryAddress인 새 장면을 추가합니다.
  2. 슬롯 채우기에서 +를 클릭하여 새 슬롯을 추가합니다.
  3. 유형 선택에서 슬롯 유형으로 actions.type.DeliveryAddressValue를 선택합니다.
  4. 슬롯 이름 필드에서 슬롯 이름을 TransactionDeliveryAddress로 지정합니다.
  5. 슬롯 값 쓰기백 맞춤설정 체크박스를 사용 설정합니다 (기본적으로 사용 설정됨).
  6. 저장을 클릭합니다.

슬롯을 구성할 때 어시스턴트의 주소 가져오기 요청 앞에 문자열로 주소를 지정할 수 있는 reason를 제공할 수 있습니다.기본 이유 문자열은 '주문을 보낼 위치를 알고자'입니다. 따라서 어시스턴트가 사용자에게 다음과 같이 물을 수 있습니다. "주문 상품을 보낼 위치를 확인하려면 배송지 주소가 필요합니다."

  • 화면이 있는 영역에서 사용자는 거래에 사용할 주소를 선택합니다. 이전에 주소를 제공한 적이 없다면 새 주소를 입력할 수 있습니다.
  • 음성 전용 노출 영역에서 어시스턴트는 사용자에게 거래의 기본 주소를 공유할 권한을 요청합니다. 사용자가 이전에 주소를 지정하지 않은 경우 대화가 전화로 핸드오프되어 참여할 수 있습니다.

배송지 주소 결과를 처리하려면 다음 단계를 따르세요.

  1. Scenes(장면) 탭에서 새로 만든 DeliveryAddress 장면을 선택합니다.
  2. 조건에서 +를 클릭하여 새 조건을 추가합니다.
  3. 텍스트 필드에 다음 조건 구문을 입력하여 성공 조건을 확인합니다.

    scene.slots.status == "FINAL" && session.params.TransactionDeliveryAddress.userDecision == "ACCEPTED"
    
  4. 방금 추가한 조건 위로 커서를 가져간 후 위쪽 화살표를 클릭하여 if scene.slots.status == "FINAL" 앞에 배치합니다.

  5. 메시지 보내기를 사용 설정하고 사용자에게 주소를 받았음을 알리는 간단한 메시지를 제공합니다.

    candidates:
      - first_simple:
          variants:
            - speech: >-
                Great! Your order will be delivered to
                $session.params.TransactionDeliveryAddress.location.postalAddress.locality
                $session.params.TransactionDeliveryAddress.location.postalAddress.administrativeArea
                $session.params.TransactionDeliveryAddress.location.postalAddress.regionCode
                $session.params.TransactionDeliveryAddress.location.postalAddress.postalCode
    
  6. Transition에서 사용자가 대화를 계속할 수 있도록 다른 장면을 선택합니다.

  7. 조건 else if scene.slots.status == "FINAL"를 선택합니다.

  8. 메시지 전송을 사용 설정하고 사용자에게 거래를 할 수 없음을 알리는 간단한 메시지를 제공합니다.

    candidates:
      - first_simple:
          variants:
            - speech: I failed to get your delivery address.
    
  9. 사용자가 거래를 할 수 없는 경우 Transition에서 EndConversation을 선택하여 대화를 종료합니다.

주문 만들기

필요한 사용자 정보를 확보하면 사용자가 주문을 작성할 수 있도록 안내하는 '장바구니 어셈블리' 환경을 빌드하게 됩니다. 모든 작업은 제품 또는 서비스에 따라 장바구니 어셈블리 흐름이 약간 다릅니다.

가장 기본적인 장바구니 조합 환경에서는 사용자가 목록에서 항목을 선택하여 주문에 추가하는 것이지만, 개발자는 사용자 환경을 단순화하도록 대화를 설계할 수 있습니다. 사용자가 간단한 예 또는 아니요 질문을 통해 최근 구매를 재주문할 수 있는 장바구니 조립 환경을 빌드할 수 있습니다. 상위 '추천' 항목 또는 '추천' 항목의 캐러셀이나 목록 카드를 사용자에게 표시할 수도 있습니다.

다양한 응답을 사용하여 사용자의 옵션을 시각적으로 표시하는 것이 좋지만 사용자가 음성만으로 장바구니를 만들 수 있도록 대화를 설계하는 것이 좋습니다. 고품질 장바구니 조립 환경의 일부 권장사항과 예는 디자인 가이드라인을 참고하세요.

주문 만들기

대화 전반에 걸쳐 사용자가 구매하려는 항목을 수집한 후 Order 객체를 구성해야 합니다.

최소한 Order는 다음을 포함해야 합니다.

  • buyerInfo - 구매하는 사용자에 대한 정보입니다.
  • transactionMerchant - 주문을 처리한 판매자에 관한 정보입니다.
  • contents: lineItems로 나열된 주문의 실제 콘텐츠입니다.
  • priceAttributes - 할인과 세금을 포함한 총 주문 비용이 포함된 주문 가격의 세부정보입니다.

장바구니를 구성하려면 Order 응답 문서를 참고하세요. 순서에 따라 다른 필드를 포함해야 할 수도 있습니다.

아래의 샘플 코드는 선택적 필드를 포함한 전체 순서를 보여줍니다.

const order = {
  createTime: '2019-09-24T18:00:00.877Z',
  lastUpdateTime: '2019-09-24T18:00:00.877Z',
  merchantOrderId: orderId, // A unique ID String for the order
  userVisibleOrderId: orderId,
  transactionMerchant: {
    id: 'http://www.example.com',
    name: 'Example Merchant',
  },
  contents: {
    lineItems: [
      {
        id: 'LINE_ITEM_ID',
        name: 'Pizza',
        description: 'A four cheese pizza.',
        priceAttributes: [
          {
            type: 'REGULAR',
            name: 'Item Price',
            state: 'ACTUAL',
            amount: {
              currencyCode: 'USD',
              amountInMicros: 8990000,
            },
            taxIncluded: true,
          },
          {
            type: 'TOTAL',
            name: 'Total Price',
            state: 'ACTUAL',
            amount: {
              currencyCode: 'USD',
              amountInMicros: 9990000,
            },
            taxIncluded: true,
          },
        ],
        notes: [
          'Extra cheese.',
        ],
        purchase: {
          quantity: 1,
          unitMeasure: {
            measure: 1,
            unit: 'POUND',
          },
          itemOptions: [
            {
              id: 'ITEM_OPTION_ID',
              name: 'Pepperoni',
              prices: [
                {
                  type: 'REGULAR',
                  state: 'ACTUAL',
                  name: 'Item Price',
                  amount: {
                    currencyCode: 'USD',
                    amountInMicros: 1000000,
                  },
                  taxIncluded: true,
                },
                {
                  type: 'TOTAL',
                  name: 'Total Price',
                  state: 'ACTUAL',
                  amount: {
                    currencyCode: 'USD',
                    amountInMicros: 1000000,
                  },
                  taxIncluded: true,
                },
              ],
              note: 'Extra pepperoni',
              quantity: 1,
              subOptions: [],
            },
          ],
        },
      },
    ],
  },
  buyerInfo: {
    email: 'janedoe@gmail.com',
    firstName: 'Jane',
    lastName: 'Doe',
    displayName: 'Jane Doe',
  },
  priceAttributes: [
    {
      type: 'SUBTOTAL',
      name: 'Subtotal',
      state: 'ESTIMATE',
      amount: {
        currencyCode: 'USD',
        amountInMicros: 9990000,
      },
      taxIncluded: true,
    },
    {
      type: 'DELIVERY',
      name: 'Delivery',
      state: 'ACTUAL',
      amount: {
        currencyCode: 'USD',
        amountInMicros: 2000000,
      },
      taxIncluded: true,
    },
    {
      type: 'TAX',
      name: 'Tax',
      state: 'ESTIMATE',
      amount: {
        currencyCode: 'USD',
        amountInMicros: 3780000,
      },
      taxIncluded: true,
    },
    {
      type: 'TOTAL',
      name: 'Total Price',
      state: 'ESTIMATE',
      amount: {
        currencyCode: 'USD',
        amountInMicros: 15770000,
      },
      taxIncluded: true,
    },
  ],
  followUpActions: [
    {
      type: 'VIEW_DETAILS',
      title: 'View details',
      openUrlAction: {
        url: 'http://example.com',
      },
    },
    {
      type: 'CALL',
      title: 'Call us',
      openUrlAction: {
        url: 'tel:+16501112222',
      },
    },
    {
      type: 'EMAIL',
      title: 'Email us',
      openUrlAction: {
        url: 'mailto:person@example.com',
      },
    },
  ],
  termsOfServiceUrl: 'http://www.example.com',
  note: 'Sale event',
  promotions: [
    {
      coupon: 'COUPON_CODE',
    },
  ],
  purchase: {
    status: 'CREATED',
    userVisibleStatusLabel: 'CREATED',
    type: 'FOOD',
    returnsInfo: {
      isReturnable: false,
      daysToReturn: 1,
      policyUrl: 'http://www.example.com',
    },
    fulfillmentInfo: {
      id: 'FULFILLMENT_SERVICE_ID',
      fulfillmentType: 'DELIVERY',
      expectedFulfillmentTime: {
        timeIso8601: '2019-09-25T18:00:00.877Z',
      },
      location: location,
      price: {
        type: 'REGULAR',
        name: 'Delivery Price',
        state: 'ACTUAL',
        amount: {
          currencyCode: 'USD',
          amountInMicros: 2000000,
        },
        taxIncluded: true,
      },
      fulfillmentContact: {
        email: 'johnjohnson@gmail.com',
        firstName: 'John',
        lastName: 'Johnson',
        displayName: 'John Johnson',
      },
    },
    purchaseLocationType: 'ONLINE_PURCHASE',
  },
};

순서 및 프레젠테이션 옵션 만들기

사용자가 주문을 확인하기 전에 제안된 주문 카드가 표시됩니다. 다양한 순서 및 표시 옵션을 설정하여 이 카드가 사용자에게 표시되는 방식을 맞춤설정할 수 있습니다.

다음은 주문 확인 카드에 있는 사용자의 이메일을 포함하여 배송지 주소가 필요한 주문을 처리하기 위한 주문 및 프레젠테이션 옵션입니다.

const orderOptions = {
      'requestDeliveryAddress': true,
      'userInfoOptions': {
        'userInfoProperties': ['EMAIL']
      }
    };

const presentationOptions = {
      'actionDisplayName': 'PLACE_ORDER'
    };

결제 매개변수 만들기

paymentParameters 객체에는 사용하려는 Google Pay 프로세서(예: Stripe, Braintree, ACI 등)에 따라 변경되는 토큰화 매개변수가 포함됩니다.

const paymentParamenters = {
      'googlePaymentOption': {
        // facilitationSpec is expected to be a serialized JSON string
        'facilitationSpec': JSON.stringify({
          'apiVersion': 2,
          'apiVersionMinor': 0,
          'merchantInfo': {
            'merchantName': 'Example Merchant',
          },
          'allowedPaymentMethods': [
            {
              'type': 'CARD',
              'parameters': {
                'allowedAuthMethods': ['PAN_ONLY', 'CRYPTOGRAM_3DS'],
                'allowedCardNetworks': [
                  'AMEX', 'DISCOVER', 'JCB', 'MASTERCARD', 'VISA'],
              },
              'tokenizationSpecification': {
                'type': 'PAYMENT_GATEWAY',
                'parameters': {
                  'gateway': 'example',
                  'gatewayMerchantId': 'exampleGatewayMerchantId',
                },
              },
            },
          ],
          'transactionInfo': {
            'totalPriceStatus': 'FINAL',
            'totalPrice': '15.77',
            'currencyCode': 'USD',
          },
        }),
      },
    };

tokenizationSpecification 객체의 콘텐츠는 결제 게이트웨이마다 다릅니다. 다음 표에서는 각 게이트웨이에서 사용하는 매개변수를 보여줍니다.

"parameters": {
  "gateway": "example",
  "gatewayMerchantId": "exampleGatewayMerchantId"
}
ACI
"parameters": {
  "gateway": "aciworldwide",
  "gatewayMerchantId": "YOUR_ENTITY_ID"
}
ADYEN
"parameters": {
  "gateway": "adyen",
  "gatewayMerchantId": "YOUR_MERCHANT_ACCOUNT_NAME"
}
ALFA-은행
"parameters": {
  "gateway": "alfabank",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
BLUE_MEDIA
"parameters": {
  "gateway": "bluemedia",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
블루스NAP
"parameters": {
  "gateway": "bluesnap",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
브레인팅
"parameters": {
  "gateway": "braintree",
  "braintree:apiVersion": "v1",
  "braintree:sdkVersion": braintree.client.VERSION,
  "braintree:merchantId": "YOUR_BRAINTREE_MERCHANT_ID",
  "braintree:clientKey": "YOUR_BRAINTREE_TOKENIZATION_KEY"
}
CHASE_PAYMENTECH
"parameters": {
  "gateway": "chase",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ACCOUNT_NUMBER"
}
결제
"parameters": {
  "gateway": "checkoutltd",
  "gatewayMerchantId": "YOUR_PUBLIC_KEY"
}
CLOUDPAYMENTS
"parameters": {
  "gateway": "cloudpayments",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
CYBERSOURCE
"parameters": {
  "gateway": "cybersource",
  "gatewayMerchantId": "YOUR_MERCHANT_ID"
}
데이터 전송
"parameters": {
  "gateway": "datatrans",
  "gatewayMerchantId": "YOUR_MERCHANT_ID"
}
EBANX
"parameters": {
  "gateway": "ebanx",
  "gatewayMerchantId": "YOUR_PUBLIC_INTEGRATION_KEY"
}
FIRST_DATA
"parameters": {
  "gateway": "firstdata",
  "gatewayMerchantId": "YOUR_MERCHANT_ID"
}
GLOBAL_PAYMENTS
"parameters": {
  "gateway": "globalpayments",
  "gatewayMerchantId": "YOUR_MERCHANT_ID"
}
GoPay
"parameters": {
  "gateway": "gopay",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
HITRUST
"parameters": {
  "gateway": "hitrustpay",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
IMSOLUTION
"parameters": {
  "gateway": "imsolutions",
  "gatewayMerchantId": "YOUR_MERCHANT_ID"
}
LYRA
"parameters": {
  "gateway": "lyra",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
MPGS
"parameters": {
  "gateway": "mpgs",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
Money_mail_RU
"parameters": {
  "gateway": "moneymailru",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
NEWEBPAY
"parameters": {
  "gateway": "newebpay",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
NEXI
"parameters": {
  "gateway": "nexi",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
NMI
"parameters": {
  "gateway": "creditcall",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
PAYSAFE
"parameters": {
  "gateway": "paysafe",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
PAYTURE
"parameters": {
  "gateway": "payture",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
PAYU
"parameters": {
  "gateway": "payu",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
PRZELEWY24
"parameters": {
  "gateway": "przelewy24",
  "gatewayMerchantId": "YOUR_MERCHANT_ID"
}
RBKmoney
"parameters": {
  "gateway": "rbkmoney",
  "gatewayMerchantId": "YOUR_MERCHANT_ID"
}
SBERBANK
"parameters": {
  "gateway": "sberbank",
  "gatewayMerchantId": "YOUR_ORGANIZATION_NAME"
}
스퀘어
"parameters": {
  "gateway": "square",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
스트립
"parameters": {
  "gateway": "stripe",
  "stripe:version": "2018-10-31",
  "stripe:publishableKey": "YOUR_PUBLIC_STRIPE_KEY"
}
TAPPAY
"parameters": {
  "gateway": "tappay",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
TINKOFF
"parameters": {
  "gateway": "tinkoff",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
UNITELLER
"parameters": {
  "gateway": "uniteller",
  "gatewayMerchantId": "YOUR_GATEWAY_MERCHANT_ID"
}
VANTIV
"parameters": {
  "gateway": "vantiv",
  "vantiv:merchantPayPageId": "YOUR_PAY_PAGE_ID",
  "vantiv:merchantOrderId": "YOUR_ORDER_ID",
  "vantiv:merchantTransactionId": "YOUR_TRANSACTION_ID",
  "vantiv:merchantReportGroup": "*web"
}
WORLDPAY
"parameters": {
  "gateway": "worldpay",
  "gatewayMerchantId": "YOUR_WORLDPAY_MERCHANT_ID"
}
YANDEX
"parameters": {
  "gateway": "yandexcheckout",
  "gatewayMerchantId": "YOUR_SHOP_ID"
}

세션 매개변수에 주문 데이터 저장

처리에서 주문 데이터를 세션 매개변수에 저장합니다. 순서 객체는 동일한 세션의 장면 전체에서 사용됩니다.

conv.session.params.order = {
    '@type': 'type.googleapis.com/google.actions.transactions.v3.TransactionDecisionValueSpec',
    order: order,
    orderOptions: orderOptions,
    presentationOptions: presentationOptions,
    paymentParameters: paymentParameters
};

주문 제안

주문을 생성한 후에는 사용자에게 표시하여 확인하거나 거부해야 합니다. 이렇게 하려면 트랜잭션 결정을 실행하는 장면으로 전환해야 합니다.

트랜잭션 결정 장면 만들기

  1. Scenes 탭에서 이름이 TransactionDecision인 새 장면을 추가합니다.
  2. 슬롯 채우기에서 +를 클릭하여 새 슬롯을 추가합니다.
  3. 유형 선택에서 슬롯 유형으로 actions.type.TransactionDecisionValue를 선택합니다.
  4. 슬롯 이름 필드에서 슬롯 이름을 TransactionDecision로 지정합니다.
  5. 슬롯 값 쓰기백 맞춤설정 체크박스를 사용 설정합니다 (기본적으로 사용 설정됨).
  6. 슬롯 구성의 드롭다운에서 세션 매개변수 사용을 선택합니다.
  7. 슬롯 구성에서 주문을 저장하는 데 사용된 세션 매개변수의 이름을 텍스트 필드 (예: $session.params.order)에 입력합니다.
  8. 저장을 클릭합니다.

TransactionDecisionValue 슬롯을 채우기 위해 어시스턴트는 전달된 Order가 '장바구니 미리보기 카드'에 직접 렌더링되는 내장 환경을 시작합니다. 사용자는 '주문하기'라고 말하거나, 거래를 거부하거나, 신용카드나 주소와 같은 결제 옵션을 변경하거나, 주문 내용 변경을 요청할 수 있습니다.

이때 사용자는 주문 변경을 요청할 수도 있습니다. 이 경우 장바구니 조립 환경을 완료한 후 처리에서 주문 변경 요청을 처리할 수 있는지 확인해야 합니다.

트랜잭션 결정 결과 처리

TransactionDecisionValue 슬롯이 채워지면 트랜잭션 결정에 대한 사용자의 답변이 세션 매개변수에 저장됩니다. 이 값에는 다음이 포함됩니다.

  • ORDER_ACCEPTED,
  • ORDER_REJECTED,
  • DELIVERY_ADDRESS_UPDATED,
  • CART_CHANGE_REQUESTED
  • USER_CANNOT_TRANSACT.

트랜잭션 결정 결과를 처리하려면 다음 단계를 따르세요.

  1. Scenes(장면) 탭에서 새로 만든 TransactionDecision 장면을 선택합니다.
  2. 조건에서 +를 클릭하여 새 조건을 추가합니다.
  3. 텍스트 필드에 다음 조건 구문을 입력하여 성공 조건을 확인합니다.

    scene.slots.status == "FINAL" && session.params.TransactionDecision.transactionDecision == "ORDER_ACCEPTED"
    
  4. 방금 추가한 조건 위로 커서를 가져간 후 위쪽 화살표를 클릭하여 if scene.slots.status == "FINAL" 앞에 배치합니다.

  5. 메시지 보내기를 사용 설정하고 사용자에게 주문이 완료되었음을 알리는 간단한 메시지를 제공합니다.

    candidates:
      - first_simple:
          variants:
            - speech: >-
                Transaction completed! Your order
                $session.params.TransactionDecision.order.merchantOrderId is all
                set!
    
  6. Transition에서 EndConversation을 선택하여 대화를 종료합니다.

  7. 조건에서 +를 클릭하여 새 조건을 추가합니다.

  8. 텍스트 필드에 다음 조건 구문을 입력하여 실패 조건을 확인합니다.

      scene.slots.status == "FINAL" && session.params.TransactionDecision.transactionDecision == "ORDER_REJECTED"
    
  9. 방금 추가한 조건 위로 커서를 가져간 후 위쪽 화살표를 클릭하여 if scene.slots.status == "FINAL" 앞에 배치합니다.

  10. 메시지 보내기를 사용 설정하고 사용자에게 주문이 거부되었음을 알리는 간단한 메시지를 제공합니다.

    candidates:
      - first_simple:
          variants:
            - speech: Look like you don't want to order anything. Goodbye.
    
  11. 전환에서 대화 종료를 선택하여 대화를 종료합니다.

  12. 조건 else if scene.slots.status == "FINAL"를 선택합니다.

  13. 메시지 전송을 사용 설정하고 사용자에게 트랜잭션을 할 수 없음을 알리는 간단한 메시지를 제공합니다.

    candidates:
      - first_simple:
          variants:
            - speech: >-
                Transaction failed with status
                $session.params.TransactionDecision.transactionDecision
    
  14. 사용자가 거래를 할 수 없는 경우 Transition에서 EndConversation을 선택하여 대화를 종료합니다.

주문을 완료하고 영수증 전송

TransactionDecisionValue 슬롯이 ORDER_ACCEPTED의 결과를 반환하면 순서를 '확인'하는 데 필요한 모든 처리 (예: 자체 데이터베이스에 유지하고 사용자에게 청구)를 즉시 실행해야 합니다.

이 응답으로 대화를 끝낼 수 있지만, 대화를 계속하려면 간단한 응답을 포함해야 합니다. 이 초기 orderUpdate를 제공하면 사용자에게 응답의 나머지 부분과 함께 '접힌 영수증 카드'가 표시됩니다. 이 카드는 사용자가 주문 내역에서 찾을 수 있는 영수증을 반영합니다.

주문 확인 과정에서 주문 객체에는 사용자에게 표시되는 주문 ID인 userVisibleOrderId가 포함될 수 있습니다. 이 필드에 merchantOrderId를 재사용할 수 있습니다.

OrderUpdate 객체의 일부에는 후속 작업 객체가 포함되어야 합니다. 이 객체는 사용자가 어시스턴트 주문 내역에서 찾을 수 있는 주문 세부정보 하단에 URL 버튼으로 나타납니다.

  • 주문마다 최소한 VIEW_DETAILS 후속 작업을 제공해야 합니다. 여기에는 모바일 앱 또는 웹사이트의 주문을 나타내는 딥 링크가 포함되어야 합니다.
  • 또한 작업 대화의 영수증 카드 외에도 거래를 수행하기 위한 모든 현지 법규를 충족하는 공식 영수증을 이메일로 보내야 합니다.

초기 주문 업데이트를 보내려면 다음 단계를 따르세요.

  1. Scenes(장면) 탭에서 TransactionDecision 장면을 선택합니다.
  2. 조건에서 성공 결과를 확인하는 조건(ORDER_ACCEPTED)을 선택합니다.

      scene.slots.status == "FINAL" && session.params.TransactionDecision.transactionDecision == "ORDER_ACCEPTED"
    
  3. 이 조건의 경우 Call your webhook(웹훅 호출)을 사용 설정하고 update_order과 같은 인텐트 핸들러 이름을 제공합니다.

  4. 웹훅 코드에서 초기 주문 업데이트를 전송하기 위한 인텐트 핸들러를 추가합니다.

    app.handle('update_order', conv => {
      const currentTime = new Date().toISOString();
      let order = conv.session.params.TransactionDecision.order;
      conv.add(new OrderUpdate({
        'updateMask': {
          'paths': [
            'purchase.status',
            'purchase.user_visible_status_label'
          ]
        },
        'order': {
          'merchantOrderId': order.merchantOrderId,
          'lastUpdateTime': currentTime,
          'purchase': {
            'status': 'CONFIRMED',
            'userVisibleStatusLabel': 'Order confirmed'
          },
        },
        'reason': 'Reason string
      }));
    });
    

주문 업데이트 보내기

전체 기간 동안 주문 상태를 사용자에게 계속 알려야 합니다. 주문 상태 및 세부정보와 함께 HTTP PTCH 요청을 Orders API에 전송하여 사용자 주문 업데이트를 전송합니다.

Orders API에 대한 비동기 요청 설정

Orders API에 대한 주문 업데이트 요청은 액세스 토큰으로 승인됩니다. Orders API에 주문 업데이트를 PATCH하려면 Actions Console 프로젝트와 연결된 JSON 서비스 계정 키를 다운로드한 다음 서비스 계정 키를 HTTP 요청의 Authorization 헤더에 전달할 수 있는 Bearer 토큰으로 교환합니다.

서비스 계정 키를 검색하려면 다음 단계를 수행합니다.

  1. Google Cloud 콘솔에서 메뉴 ⇧ > API 및 서비스 > 사용자 인증 정보 > 사용자 인증 정보 만들기 > 서비스 계정 키로 이동합니다.
  2. 서비스 계정에서 새 서비스 계정을 선택합니다.
  3. 서비스 계정을 service-account로 설정합니다.
  4. 역할프로젝트 > 소유자로 설정합니다.
  5. 키 유형을 JSON으로 설정합니다.
  6. 만들기를 선택합니다.
  7. 비공개 JSON 서비스 계정 키가 로컬 머신에 다운로드됩니다.

주문 업데이트 코드에서 Google API 클라이언트 라이브러리와 "https://www.googleapis.com/auth/actions.order.developer" 범위를 사용하여 서비스 키를 Bearer 토큰으로 교환할 수 있습니다. API 클라이언트 라이브러리 GitHub 페이지에서 설치 단계 및 예시를 확인할 수 있습니다.

Node.js 샘플에서 키 교환 예시로 order-update.js를 참조할 수도 있습니다.

주문 업데이트 보내기

서비스 계정 키를 OAuth Bearer 토큰으로 교환하면 주문 업데이트를 승인된 PATCH 요청으로 Orders API에 전송할 수 있습니다.

Order API URL: PATCH https://actions.googleapis.com/v3/orders/${orderId}

요청에 다음 헤더를 제공합니다.

  • "Authorization: Bearer token"를 서비스 계정 키를 교환한 OAuth Bearer 토큰으로 바꿉니다.
  • "Content-Type: application/json".

PATCH 요청은 다음 형식의 JSON 본문을 사용해야 합니다.

{ "orderUpdate": OrderUpdate }

OrderUpdate 객체는 다음과 같은 최상위 필드로 구성됩니다.

  • updateMask - 업데이트하는 주문의 필드입니다. 주문 상태를 업데이트하려면 값을 purchase.status, purchase.userVisibleStatusLabel로 설정하세요.
  • order - 업데이트 콘텐츠입니다. 주문 내용을 업데이트하는 경우 업데이트된 Order 객체로 값을 설정하세요. 주문 상태를 업데이트하는 경우 (예: "CONFIRMED"에서 "SHIPPED"로) 객체에는 다음 필드가 포함됩니다.

    • merchantOrderId: Order 객체에서 설정한 것과 동일한 ID입니다.
    • lastUpdateTime - 이 업데이트의 타임스탬프입니다.
    • purchase - 다음을 포함하는 객체입니다.
      • status - 주문 상태를 PurchaseStatus로 나타냅니다(예: 'SHIPPED' 또는 'DELIVERED').
      • userVisibleStatusLabel - 주문 상태에 대한 세부정보를 제공하는 사용자 대상 라벨(예: '주문이 발송되었으며 배송 중입니다.')
  • userNotification 객체. 이 객체를 포함한다고 해서 알림이 사용자 기기에 표시되는 것은 아닙니다.

다음 샘플 코드는 주문 상태를 DELIVERED로 업데이트하는 OrderUpdate 예를 보여줍니다.

// Import the 'googleapis' module for authorizing the request.
const {google} = require('googleapis');
// Import the 'request-promise' module for sending an HTTP POST request.
const request = require('request-promise');
// Import the OrderUpdate class from the client library.
const {OrderUpdate} = require('@assistant/conversation');

// Import the service account key used to authorize the request.
// Replacing the string path with a path to your service account key.
// i.e. const serviceAccountKey = require('./service-account.json')

// Create a new JWT client for the Actions API using credentials
// from the service account key.
let jwtClient = new google.auth.JWT(
    serviceAccountKey.client_email,
    null,
    serviceAccountKey.private_key,
    ['https://www.googleapis.com/auth/actions.order.developer'],
    null,
);

// Authorize the client
let tokens = await jwtClient.authorize();

// Declare order update
const orderUpdate = new OrderUpdate({
    updateMask: {
      paths: [
        'purchase.status',
        'purchase.user_visible_status_label'
      ]
    },
    order: {
      merchantOrderId: orderId, // Specify the ID of the order to update
      lastUpdateTime: new Date().toISOString(),
      purchase: {
        status: 'DELIVERED',
        userVisibleStatusLabel: 'Order delivered',
      },
    },
    reason: 'Order status updated to delivered.',
});

// Set up the PATCH request header and body,
// including the authorized token and order update.
let options = {
  method: 'PATCH',
  uri: `https://actions.googleapis.com/v3/orders/${orderId}`,
  auth: {
    bearer: tokens.access_token,
  },
  body: {
    header: {
      isInSandbox: true,
    },
    orderUpdate,
  },
  json: true,
};

// Send the PATCH request to the Orders API.
try {
  await request(options);
} catch (e) {
  console.log(`Error: ${e}`);
}
구매 상태 설정

주문 업데이트의 status은 주문의 현재 상태를 설명해야 합니다. 업데이트의 order.purchase.status 필드에서 다음 값 중 하나를 사용합니다.

  • CREATED - 주문이 사용자가 수락하고 작업 관점에서 '생성'되었지만 백엔드에서 수동 처리가 필요합니다.
  • CONFIRMED - 주문이 활성 상태이며 처리를 위해 처리 중입니다.
  • IN_PREPARATION - 음식 요리나 상품 포장 등 주문 상품의 배송/배송 준비 중입니다.
  • READY_FOR_PICKUP - 수령인이 주문 상품을 수령할 수 있습니다.
  • DELIVERED - 주문이 수취인에게 배송되었습니다.
  • OUT_OF_STOCK - 주문한 하나 이상의 상품이 재고가 없습니다.
  • CHANGE_REQUESTED: 사용자가 주문 변경을 요청했으며 변경사항을 처리하는 중입니다.
  • RETURNED - 배송 후 사용자가 주문을 반품했습니다.
  • REJECTED - 주문을 처리, 청구 또는 '활성화'할 수 없는 경우
  • CANCELLED - 사용자가 주문을 취소했습니다.

거래와 관련된 각 상태에 대해 주문 업데이트를 전송해야 합니다. 예를 들어 거래 완료 후 주문을 로깅하기 위해 수동 처리가 필요한 경우 추가 처리가 완료될 때까지 CREATED 주문 업데이트를 전송합니다. 모든 주문에 상태 값이 필요한 것은 아닙니다.

프로젝트 테스트

프로젝트를 테스트할 때 Actions 콘솔에서 샌드박스 모드를 사용 설정하여 결제 수단에 요금을 청구하지 않고 작업을 테스트할 수 있습니다. 샌드박스 모드를 사용 설정하려면 다음 단계를 따르세요.

  1. Actions 콘솔의 탐색 메뉴에서 Test를 클릭합니다.
  2. 설정을 클릭합니다.
  3. 개발 샌드박스 옵션을 사용 설정합니다.

실제 거래의 경우 샘플에서 isInSandbox 필드를 true로 설정할 수도 있습니다. 이 작업은 Actions 콘솔에서 샌드박스 모드 설정을 사용 설정하는 것과 같습니다. isInSandbox를 사용하는 코드 스니펫을 보려면 주문 업데이트 전송 섹션을 참고하세요.

문제 해결

테스트 중에 문제가 발생하면 트랜잭션의 문제 해결 단계를 참고하세요.