Apple和Google退款信息通知

退款的濫用

針對退款,不同國家或地區(qū)會有不同的“無條件退款期限”,例如蘋果

  • AppStore商店退款政策:

中國/美國/韓國 等大多數(shù)國家:90天有條件退款

  • 在中國區(qū)AppStore的具體退款政策:一個(gè)ID有1次無條件退款條件,一年2次有條件退款,第3次退款會非常難。至于退款到賬時(shí)間快為36小時(shí),也有7-15個(gè)工作日退還。
  • 正是這些“漏洞”,所以出現(xiàn)專業(yè)的代充工作室,導(dǎo)致開發(fā)者壞賬非常嚴(yán)重。特別是火爆的游戲代充和直播行業(yè)。

退款的具體手段

  1. 利用淘寶店以代充打折的形式獲取玩家的游戲賬號,為玩家充值后申請退款。淘寶店獲得充值金,玩家獲得游戲商品,最終虧損的是游戲廠商。
  2. 收購消費(fèi)過的App Store ID賬號,收到的賬號會被用來退款和直播打賞,主播可以自己刷火箭然后申請退款。

針對海外支付,退款的方式主要是App Store和Google

App Store

  • AppStore退款****通知參考文檔
    蘋果會定期通知****退款內(nèi)容,當(dāng)用戶退款后蘋果會調(diào)用配置的接口通知我們(被動(dòng)接收)
  • 配置url
    1. 登錄蘋果后臺 https://appstoreconnect.apple.com/
    2. 選擇需要通知的app應(yīng)用,點(diǎn)擊側(cè)邊欄的綜合->App信息
    3. 在AppStore服務(wù)器通知網(wǎng)址(URL)中配置我們的接口地址(必須為Https)
蘋果響應(yīng)信息

responsebody通知參數(shù)詳細(xì)文檔
從App Store在服務(wù)器通知中返回的是一條JSON數(shù)據(jù)的退款****通知,例子:

{
    "notification_type": "REFUND",#觸發(fā)通知的訂閱事件。REFUND:退款
    "environment": "PROD", #App Store生成收據(jù)的環(huán)境,可能是沙箱和生產(chǎn)環(huán)境
    "latest_receipt": "",#不推薦使用。自2021年3月10日起,生產(chǎn)和沙箱環(huán)境中將不再提供此對象。
    "latest_receipt_info": {},#不推薦使用。自2021年3月10日起,生產(chǎn)和沙箱環(huán)境中將不再提供此對象。
    "unified_receipt": {
        "status": 0,
        "environment": "Production",
        "latest_receipt_info": [{#最近100筆退款訂單信息
            "quantity": "1",
            "product_id": "com.xxxxxx.xmios.60",
            "transaction_id": "490022793443160", #蘋果訂單號
            "purchase_date": "2021-04-01 18:04:09 Etc/GMT",
            "purchase_date_ms": "1617300249000",
            "purchase_date_pst": "2021-04-01 11:04:09 America/Los_Angeles",
            "original_purchase_date": "2021-04-01 18:04:09 Etc/GMT",
            "original_purchase_date_ms": "1617300249000",
            "original_purchase_date_pst": "2021-04-01 11:04:09 America/Los_Angeles",
            "is_trial_period": "false",
            "original_transaction_id": "490000793443160",
            "cancellation_date": "2021-04-02 16:24:42 Etc/GMT", #退款時(shí)間
            "cancellation_date_ms": "1617380682000", #退款時(shí)間 精確到毫秒
            "cancellation_date_pst": "2021-04-02 09:24:42 America/Los_Angeles",
            "cancellation_reason": "1",
            "in_app_ownership_type": "PURCHASED"
        }],
        "latest_receipt": "" #不推薦使用。自2021年3月10日起,生產(chǎn)和沙箱環(huán)境中將不再提供此對象。
    },
    "bid": "com.xxxxxx.xmios",
    "bvrs": "2.20"
}

這里我主要關(guān)注兩個(gè)字段

  1. cancellation_date(退款時(shí)間)
  2. transaction_id(蘋果訂單號)
    unified_receipt 存放著最近100筆退款****訂單信息,我們可以循環(huán)遍歷數(shù)組,通過數(shù)組下的 transaction_id 從數(shù)據(jù)庫中查到訂單信息,結(jié)合 cancellation_date 保存到退款記錄表。
Google
class GoogleRefundCommand extends Command
{
    protected $signature = 'google:refund {startTime?} {endTime?}';

    private $key =  "",//使用JSON里的密鑰
 
    private $iss = ''; //服務(wù)帳戶的電子郵件地址。

    private $sub = ''; //應(yīng)用程序正在請求委派訪問權(quán)限的用戶的電子郵件地址。

    private $scope = 'https://www.googleapis.com/auth/androidpublisher';//以空格分隔的應(yīng)用程序請求的權(quán)限列表。

    private $aud = 'https://oauth2.googleapis.com/token';//聲明的預(yù)期目標(biāo)的描述符。發(fā)出訪問令牌請求時(shí),此值始終為。https://oauth2.googleapis.com/token

    private $package_name = ['','',''];

    private $getTokenUrl = 'https://www.googleapis.com/androidpublisher/v3/applications/';

    private $getTokenMethod = '/purchases/voidedpurchases';

    private $client = "";


    public function __construct()
    {
        parent::__construct();
        $this->client = HttpAgent::getInstance();
    }


    private function base64url_encode($data)
    {
        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
    }


    /**
     * 谷歌退款
     * 獲取access_token 接口請求文檔 https://developers.google.com/identity/protocols/oauth2/service-account
     * @return mixed
     */
    public function handle()
    {
        //只在線上執(zhí)行
        if (config('app.env') != 'production') {
            Log::info('獲取谷歌獲取退款腳本執(zhí)行時(shí)間:' . date('Y-m-d H:i:s'));
            return;
        }
        //頭部
        $header = [
            'alg' => 'RS256', //生成signature的算法
            'typ' => 'JWT'    //類型
        ];
        $payload = ['iss' => $this->iss, 'sub' => $this->sub, 'scope' => $this->scope, 'aud' => $this->aud, 'iat' => time(), 'exp' => time() + 1800];
        //  {Base64url encoded JSON header}
        $jwtHeader = $this->base64url_encode(json_encode($header));
        //  {Base64url encoded JSON claim set}
        $jwtClaim = $this->base64url_encode(json_encode($payload));
        //  The base string for the signature: {Base64url encoded JSON header}.{Base64url encoded JSON claim set}
        openssl_sign($jwtHeader . "." . $jwtClaim, $jwtSig, $this->key, "sha256WithRSAEncryption");
        $jwtSign = $this->base64url_encode($jwtSig);
        //  {Base64url encoded JSON header}.{Base64url encoded JSON claim set}.{Base64url encoded signature}
        $jwtAssertion = $jwtHeader . "." . $jwtClaim . "." . $jwtSign;
        
        dd($jwtAssertion);
        // todo...
       $ret = $this->client->request('post', 'https://oauth2.googleapis.com/token', ['query' => [
            'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
            'assertion' => $jwtAssertion,
        ]]);
    }
}

這里我們使用postman測試一下
1. 請求 https://oauth2.googleapis.com/token 接口
2.grant_type = urn:ietf:params:oauth:grant-type:jwt-bearer,assertion使用 $jwtAssertion 參數(shù)

image.png

保存返回的授權(quán)令牌access_token
使用GET請求https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/voidedpurchases

image.png

startTime:您想在響應(yīng)中看到的最早作廢的購買交易的時(shí)間。默認(rèn)情況下,startTime 設(shè)為 30 天以前。注意:這里的startTime是毫秒
maxResults:每個(gè)響應(yīng)中出現(xiàn)的已作廢購買交易的數(shù)量上限。默認(rèn)情況下,此值為 1000。請注意,此參數(shù)的最大值也是 1000。
token: 之前響應(yīng)中的繼續(xù)令牌;可讓您查看更多結(jié)果。

Google響應(yīng)信息

Google響應(yīng)信息文檔

{
  "tokenPagination": {
    "nextPageToken": "next_page_token"
  },
  "voidedPurchases": [
    {
      "kind": "androidpublisher#voidedPurchase",
      "purchaseToken": "some_purchase_token",
      "purchaseTimeMillis": "1468825200000",
      "voidedTimeMillis": "1469430000000",
      "orderId": "some_order_id",
      "voidedSource": "0",
      "voidedReason": "4"
    },
    {
      "kind": "androidpublisher#voidedPurchase",
      "purchaseToken": "some_other_purchase_token",
      "purchaseTimeMillis": "1468825100000",
      "voidedTimeMillis": "1470034800000",
      "orderId": "some_other_order_id",
      "voidedSource": "2",
      "voidedReason": "5"
    },
  ]
}

這里我主要關(guān)注兩個(gè)字段
voidedTimeMillis(退款時(shí)間)
orderId(Google訂單號)
voidedPurchases存放著maxResults條退款訂單信息,如果結(jié)果數(shù)量超過了在 maxResults請求參數(shù)中指定的數(shù)量,響應(yīng)就會包含一個(gè) nextPageToken值,這里我寫了一個(gè)遞歸函數(shù)判斷 nextPageToken是否為空,非空則將該值傳遞給后續(xù)請求來查看更多結(jié)果。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容