Webhooks cho Thanh toán

Cập nhật trong thời gian thực về các giao dịch của bạn.

Webhooks cho Thanh toán (trước đây là Cập nhật trong thời gian thực) là phương thức thiết yếu để thông báo cho bạn về những thay đổi đối với đơn đặt hàng được thực hiện qua dịch vụ Thanh toán trên Facebook trong ứng dụng của bạn.

Tổng quan

Webhooks là một hệ thống dựa trên gói đăng ký giữa Facebook và máy chủ của bạn. Ứng dụng của bạn đăng ký nhận thông tin cập nhật từ Facebook qua điểm cuối HTTPS đã chỉ định. Khi một đơn đặt hàng đã thực hiện trong ứng dụng của bạn được cập nhật, chúng tôi sẽ gửi yêu cầu HTTPS POST đến điểm cuối đó nhằm thông báo cho máy chủ của bạn về sự thay đổi.

Có 3 trường hợp chính mà thông tin cập nhật sẽ được gửi đến máy chủ nhà phát triển của bạn:

Đăng ký Webhooks

Để đăng ký Webhooks cho Thanh toán, trước tiên, hãy tạo một URL điểm cuối công khai sẽ nhận cả HTTPS GET để xác minh gói đăng ký và POST để yêu cầu thay đổi dữ liệu. Cấu trúc của cả hai loại yêu cầu này được mô tả bên dưới. Tiếp theo, hãy thiết lập gói đăng ký đối tượng payment của ứng dụng. Bạn có 2 cách để thực hiện việc này:

Trong cả hai trường hợp, điểm cuối của bạn sẽ nhận được cùng dữ liệu theo cùng một cách. Hãy xem Máy chủ gọi lại của bạn để biết thêm thông tin về nội dung mà máy chủ sẽ nhận được.

Đăng ký qua Bảng điều khiển ứng dụng

Cách dễ nhất để thiết lập ứng dụng nhằm nhận thông tin cập nhật từ Webhooks là sử dụng bảng điều khiển Thanh toán của Bảng điều khiển ứng dụng. Tìm ứng dụng của bạn trong bảng điều khiển, sau đó nhấp vào tab Payments. Phần Webhooks sẽ nằm ngay dưới phần Cài đặt của công ty bạn.

Webhooks cho Thanh toán

Sau đó, màn hình này sẽ liệt kê trạng thái đăng ký của ứng dụng, cho dù đăng ký được thêm qua bảng điều khiển này hay qua API. Từ đây, bạn có thể thay đổi và thử nghiệm URL gọi lại gói đăng ký.

Trong trường "Gọi lại", bạn phải cung cấp điểm cuối máy chủ hợp lệ có thể truy cập công khai. Đây là địa chỉ mà chúng tôi sẽ dùng để xác minh gói đăng ký cũng như gửi thông tin cập nhật. Điểm cuối này cần phản hồi như được mô tả trong Máy chủ gọi lại của bạn.

Cuối cùng, hãy cung cấp "Mã xác minh". Mã này sẽ chỉ được gửi trong giai đoạn đăng ký để xác minh rằng bạn đang thực hiện đăng ký từ một vị trí an toàn. Mã này sẽ không được gửi khi có thông tin cập nhật thông thường từ Webhook.

Thử nghiệm cài đặt của bạn

Bạn phải thử nghiệm cài đặt gọi lại trước khi lưu gói đăng ký. Thao tác này sẽ gửi yêu cầu GET xác minh đến điểm cuối của bạn. Yêu cầu này chứa các thông số hub.mode, hub.challengehub.verify_token, đồng thời đảm bảo rằng bạn xử lý chúng đúng cách. Ví dụ: bạn phải đảm bảo điểm cuối sẽ trả về hub.challenge cho Facebook:

Thử nghiệm cài đặt của bạn

Sau khi nhập chi tiết gói đăng ký, hãy nhớ nhấp vào nút "Lưu thay đổi" ở cuối trang. Chỉnh sửa gói đăng ký là một thao tác đơn giản để sửa đổi nội dung trường, thử nghiệm lại rồi lưu lại mẫu.

Đăng ký qua API Đồ thị

Bạn cũng có thể thiết lập và liệt kê gói đăng ký theo lập trình thông qua API Đồ thị. Bạn sẽ cần access token của ứng dụng. Bạn có thể lấy mã này trong Công cụ tạo mã truy cập hoặc qua điểm cuối /oauth của API Đồ thị

API Đăng ký có sẵn trên điểm cuối https://graph.facebook.com/[APP_ID]/subscriptions

Với API này, bạn có thể thực hiện 3 tác vụ:

  • Thêm hoặc sửa đổi gói đăng ký (bằng cách gửi yêu cầu HTTPS POST)
  • Liệt kê từng gói đăng ký hiện có của bạn (bằng cách gửi yêu cầu HTTPS GET)

Thêm và sửa đổi gói đăng ký

Để thiết lập gói đăng ký, hãy gửi POST kèm theo thông số sau. Lưu ý rằng những thông số này tương ứng với các trường trong mẫu được mô tả ở trên:

  • object - Giống như trên, đây là loại đối tượng bạn muốn nhận thông tin cập nhật. Chỉ định payments.
  • fields - Danh sách các thuộc tính của loại đối tượng mà bạn muốn nhận thông tin cập nhật về thay đổi. Danh sách này được phân tách bằng dấu phẩy. Chỉ định "hành động" và "tranh chấp".
  • callback_url - Điểm cuối máy chủ hợp lệ và có thể truy cập công khai.
  • verify_token - Một chuỗi tùy ý được gửi đến điểm cuối của bạn khi xác minh gói đăng ký.

Khi nhận được yêu cầu này, giống với cấu hình mẫu ở trên, chúng tôi sẽ gửi yêu cầu GET đến điểm cuối gọi lại của bạn để đảm bảo rằng đăng ký hợp lệ và sẵn sàng nhận thông tin cập nhật. Cụ thể, bạn phải đảm bảo điểm cuối của mình trả về hub.challenge cho Facebook.

Lưu ý rằng mỗi loại đối tượng của ứng dụng chỉ có thể có một gói đăng ký. Vì thế, nếu một gói đăng ký đã tồn tại cho loại đối tượng này thì dữ liệu mới đăng sẽ thay thế bất kỳ dữ liệu hiện có nào.

Liệt kê các gói đăng ký của bạn

Việc gửi yêu cầu HTTP GET đến API Đăng ký sẽ trả về nội dung được mã hóa JSON liệt kê các gói đăng ký của bạn. Ví dụ:

[
  {
    "object": "payments",
    "callback_url": "https://www.friendsmash.com/rtu.php",
    "fields": ["actions", "disputes"],
    "active": true
  }
]

Bạn có thể sử dụng Trình khám phá đồ thị để thử nghiệm trực tiếp với API này, hãy nhớ sử dụng mã truy cập của ứng dụng.

Máy chủ gọi lại

Máy chủ gọi lại của bạn phải xử lý 2 loại yêu cầu. Hãy đảm bảo rằng máy chủ gọi lại đó nằm trên một URL công khai để chúng tôi có thể gửi thành công những yêu cầu này.

Xác minh gói đăng ký

Trước tiên, máy chủ Facebook sẽ tạo một HTTPS GET đến URL gọi lại của bạn khi bạn cố gắng thêm hoặc sửa đổi gói đăng ký. Một chuỗi truy vấn sẽ được thêm vào URL gọi lại của bạn kèm theo các thông số sau:

Thông số Mô tả

hub.mode

Chuỗi "subscribe" được chuyển vào thông số này

hub.challenge

Chuỗi ngẫu nhiên

hub.verify_token

Giá trị verify_token mà bạn đã chỉ định khi tạo gói đăng ký

Trước tiên, điểm cuối này phải xác minh hub.verify_token. Việc này đảm bảo máy chủ của bạn biết rằng yêu cầu là do Facebook tạo và có liên quan đến gói đăng ký mà bạn vừa đặt cấu hình.

Sau đó, máy chủ sẽ chỉ trả về giá trị hub.challenge, qua đó xác nhận với Facebook rằng máy chủ này được đặt cấu hình để chấp nhận các lệnh gọi lại và ngăn chặn lỗ hổng từ chối dịch vụ (DDoS).

Lưu ý dành cho nhà phát triển PHP: Trong PHP, các dấu chấm và dấu cách trong tên thông số truy vấn sẽ tự động được chuyển đổi thành dấu gạch chân. Do đó, bạn nên truy cập những thông số này thông qua $_GET['hub_mode'],$_GET['hub_challenge']$_GET['hub_verify_token'] nếu đang ghi điểm cuối gọi lại trong PHP. Hãy xem lưu ý này trong hướng dẫn về ngôn ngữ PHP để biết thêm chi tiết.

Nhận thông tin cập nhật

Sau khi đăng ký thành công, chúng tôi sẽ tiếp tục gửi HTTPS POST đến điểm cuối máy chủ của bạn mỗi khi có thay đổi (đối với các trường hoặc kết nối đã chọn). Bạn phải phản hồi yêu cầu này kèm theo mã HTTP 200.

Lưu ý - chúng tôi xem bất kỳ phản hồi HTTP nào không phải 200 đều là lỗi. Trong những trường hợp này, chúng tôi sẽ tiếp tục thử gửi lại thông tin cập nhật từ webhook. Do đó, nếu không phản hồi đúng, bạn có thể nhận được cùng một thông tin cập nhật nhiều lần.

Yêu cầu sẽ có loại nội dung là application/json và phần nội dung sẽ bao gồm chuỗi được mã hóa JSON chứa một hoặc nhiều thay đổi.

Lưu ý dành cho nhà phát triển PHP: Trong PHP, để nhận dữ liệu được mã hóa, bạn cần sử dụng mã sau:

$data = file_get_contents("php://input");
$json = json_decode($data);`

Lưu ý rằng bạn không thể gửi lại các thông số hub.mode, hub.challengehub.verify_token sau khi gói đăng ký được xác nhận.

Dưới đây là ví dụ điển hình về lệnh gọi lại được tạo cho gói đăng ký đối tượng payments:

{
  "object": "payments",
  "entry": [
    {
      "id": "296989303750203",
      "time": 1347996346,
      "changed_fields": [
        "actions"
      ]
    }
  ]
}

Cần lưu ý rằng thông tin cập nhật từ Webhook chỉ thông báo cho bạn về một khoản thanh toán cụ thể - được xác định bằng trường id - đã được thay đổi. Sau khi nhận được thông tin cập nhật, bạn sẽ phải truy vấn API Đồ thị để biết chi tiết về giao dịch, nhằm xử lý thay đổi theo cách phù hợp.

Lưu ý - Mặc dù Webhooks cho các loại đối tượng khác có thể được tạo theo lô nhưng thông tin cập nhật về thanh toán sẽ không bao giờ được tạo theo lô.

Bạn chắc chắn sẽ nhận được thông tin cập nhật mới mỗi khi một giao dịch được cập nhật do hành động của người dùng hoặc hành động của nhà phát triển.

Nếu không gửi được thông tin cập nhật đến máy chủ của bạn, chúng tôi sẽ thử lại ngay rồi thử lại thêm vài lần nữa với tần suất giảm dần trong 24 giờ tiếp theo.

Với mọi yêu cầu, chúng tôi sẽ gửi tiêu đề HTTP X-Hub-Signature-256 chứa chữ ký SHA256 của phần tải dữ liệu yêu cầu bằng cách sử dụng khóa bí mật của ứng dụng làm khóa và có tiền tố là sha256=. Điểm cuối gọi lại của bạn có thể xác minh chữ ký này để xác thực tính toàn vẹn và nguồn gốc của phần tải dữ liệu.

Phản hồi thông tin cập nhật

Sau khi máy chủ của bạn nhận được thông tin cập nhật, bạn cần truy vấn API Đồ thị bằng cách sử dụng trường id để biết chi tiết về trạng thái mới của giao dịch. Sau đó, bạn cần thực hiện hành động tùy thuộc vào trạng thái.

Những phần sau liệt kê tất cả các thay đổi trạng thái có thể kích hoạt quá trình gửi thông tin cập nhật. Những thay đổi này nhìn chung được chia thành:

  • Các thay đổi đối với mảng hành động - diễn ra khi khoản thanh toán hoàn tất không đồng bộ, tiền hoàn lại được cấp (bởi bạn hoặc Facebook) hoặc khi có yêu cầu hoàn tiền.
  • Các thay đổi đối với mảng tranh chấp - diễn ra khi người tiêu dùng có tranh chấp về đơn đặt hàng.

Hành động

Mỗi đối tượng payment chứa một mảng có tên là actions. Mảng này chứa tập hợp các thay đổi trạng thái mà giao dịch đã trải qua. Mỗi mục nhập trong mảng actions chứa thuộc tính có tên là type mô tả loại hành động đã diễn ra. type có thể có các giá trị sau: charge, refund,chargeback, chargeback_reversaldecline - được giải thích đầy đủ tại đây.

Dưới đây là phản hồi mẫu từ API Đồ thị cho đối tượng thanh toán có các hành động được liên kết:

{
   "id": "3603105474213890",
   "user": {
      "name": "Marco Alvarez",
      "id": "500535225"
   },
   "application": {
      "name": "Friend Smash",
      "namespace": "friendsmashsample",
      "id": "241431489326925"
   },
   "actions": [
      {
         "type": "charge",
         "status": "completed",
         "currency": "USD",
         "amount": "0.99",
         "time_created": "2013-03-22T21:18:54+0000",
         "time_updated": "2013-03-22T21:18:55+0000"
      },
      {
         "type": "refund",
         "status": "completed",
         "currency": "USD",
         "amount": "0.99",
         "time_created": "2013-03-23T21:18:54+0000",
         "time_updated": "2013-03-23T21:18:55+0000"
      }
   ],
   "refundable_amount": {
      "currency": "USD",
      "amount": "0.00"
   },
   "items": [
      {
         "type": "IN_APP_PURCHASE",
         "product": "https://www.friendsmash.com/og/friend_smash_bomb.html",
         "quantity": 1
      }
   ],
   "country": "US",
   "created_time": "2013-03-22T21:18:54+0000",
   "payout_foreign_exchange_rate": 1,}`

Vì bạn đăng ký trường hành động khi đăng ký Webhooks, chúng tôi sẽ gửi thông tin cập nhật khi mảng này thay đổi như sau:

Phí

Ban đầu, tất cả các đơn đặt hàng đều chứa mục nhập phí có "status": "initiated". Khoản thanh toán đã bắt đầu biểu thị khoản thanh toán chỉ được tạo và chưa hoàn thành đầy đủ. Chúng tôi sẽ không gửi thông tin cập nhật về các khoản thanh toán ở trạng thái đã bắt đầu.

Khi hoàn tất thanh toán thành công, "status": "initiated" sẽ được thay đổi thành "status": "completed" và chúng tôi sẽ gửi thông tin cập nhật. Khi nhìn thấy thay đổi này, bạn nên kiểm tra hồ sơ thanh toán để xác minh xem đây là giao dịch mới hay cũ và phản hồi như sau:

  • Nếu bạn đã biết đơn đặt hàng và đơn đặt hàng đã được thực hiện bởi lệnh gọi lại JavaScript (ưu tiên làm lựa chọn đầu tiên) thì bạn có thể yên tâm bỏ qua thông tin cập nhật hoặc dùng thông tin đó để xác nhận thêm.
  • Nếu bạn đã biết đơn đặt hàng nhưng đơn đặt hàng lại có trạng thái initiated thì bạn có thể tiếp tục thực hiện đơn đặt hàng, qua đó gửi mặt hàng hoặc đơn vị tiền tệ ảo được liên kết cho người tiêu dùng. Sau đó, bạn có thể yên tâm đánh dấu khoản thanh toán này là hoàn tất.
  • Nếu đơn đặt hàng không xác định thì tức là quy trình phía máy khách chưa hoàn tất, nhiều khả năng là do sự cố kết nối hoặc người tiêu dùng đóng trình duyệt trong khi đang thanh toán. Bạn vẫn có thể thực hiện và hoàn tất đơn đặt hàng này một cách an toàn bởi vì Facebook vẫn là nguồn dữ liệu đáng tin cậy nhất liên quan đến lập hóa đơn người dùng.

Bạn cũng sẽ nhận được thông tin cập nhật về khoản thanh toán có "status": "failed". Những khoản thanh toán này sẽ không được thực hiện.

Hoàn tiền

Mỗi khi hoàn tiền qua API Đồ thị, bạn sẽ nhận được thông tin cập nhật. Giống như với "type": "charge", quá trình hoàn tiền cũng có thể có trạng thái khác nhau mà bạn phải biết. Đáng chú ý nhất là quá trình hoàn tiền có thể không thực hiện được - thường là do lỗi kết nối hoặc xử lý - trong trường hợp này, bạn nên thử lại quá trình hoàn tiền.

Yêu cầu hoàn tiền, từ chối và hủy bỏ yêu cầu hoàn tiền

Giống với tiền hoàn lại, bạn cũng sẽ nhận được thông báo khi có yêu cầu hoàn tiền, từ chối hoặc hủy bỏ yêu cầu hoàn tiền. Đối tượng yêu cầu hoàn tiền, từ chối hoặc hủy bỏ yêu cầu hoàn tiền sẽ được thêm vào mảng hành động của dữ liệu trả về API Đồ thị cho khoản thanh toán.

Tranh chấp

Chúng tôi sẽ thông báo cho bạn bằng cách gửi thông tin cập nhật khi có tranh chấp. Trong trường hợp này, bạn sẽ thấy mảng "disputes" mới xuất hiện như là một phần của đối tượng payment. Mảng này sẽ chứa thời gian phát sinh tranh chấp, lý do mà người tiêu dùng đưa ra trong phản hồi và địa chỉ email của người tiêu dùng. Bạn có thể dùng những thông tin này để liên hệ trực tiếp với người tiêu dùng nhằm giải quyết tranh chấp.

Dưới đây là phản hồi mẫu đầy đủ từ API Đồ thị cho giao dịch có tranh chấp:

{
   "id": "990361254213890",
   "user": {
      "name": "Marco Alvarez",
      "id": "500535225"
   },
   "application": {
      "name": "Friend Smash",
      "namespace": "friendsmashsample",
      "id": "241431489326925"
   },
   "actions": [
      {
         "type": "charge",
         "status": "completed",
         "currency": "USD",
         "amount": "0.99",
         "time_created": "2013-03-22T21:18:54+0000",
         "time_updated": "2013-03-22T21:18:55+0000"
      }
   ],
   "refundable_amount": {
      "currency": "USD",
      "amount": "0.99"
   },
   "items": [
      {
         "type": "IN_APP_PURCHASE",
         "product": "https://www.friendsmash.com/og/friend_smash_bomb.html",
         "quantity": 1
      }
   ],
   "country": "US",
   "created_time": "2013-03-22T21:18:54+0000",
   "payout_foreign_exchange_rate": 1,
   "disputes": [
      {
         "user_comment": "I didn't receive my item! I want a refund, please!",
         "time_created": "2013-03-24T18:21:02+0000",
         "user_email": "email\u0040domain.com",
         "status": "resolved", 
         "reason": "refunded_in_cash"
      }
   ]
}

Để biết thêm thông tin về cách phản hồi tranh chấp và tiến hành hoàn tiền, vui lòng xem Hướng dẫn về thanh toán: Xử lý tranh chấp và hoàn tiền.