使用 Android 发送条带令牌的正确方法

the correct way of sending a stripe token with Android

里面有很多信息!

我知道有多个线程在讨论这个问题,但没有一个得到正确回答,而且 Stripe 自己的教程和参考资料也好不到哪儿去。所以希望我(和其他人)可以最终看到这个对我们业余开发者来说这个长期存在的问题的曙光。

我已经尝试实施 Stripe 的 API 超过 2 周了,但仍然遇到同样的问题。我的 Android 代码一直在正常工作,直到我从 Stripe 收到我的令牌。

public class Checkout extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_checkout);
    }

    public void submitCard(View view) throws AuthenticationException {


        TextView cardNumberField = (TextView) findViewById(R.id.cardNumber);
        TextView monthField = (TextView) findViewById(R.id.month);
        TextView yearField = (TextView) findViewById(R.id.year);
        TextView cvcField = (TextView) findViewById(R.id.cvc);

        Card card = new Card(cardNumberField.getText().toString(), Integer.valueOf(monthField.getText().toString()), Integer.valueOf(yearField.getText().toString()), cvcField.getText().toString());
        //Card newCard = new Card("4242 4242 4242 4242", 12, 19, "123");

        Stripe stripe = new Stripe("pk_test_key");
        stripe.createToken(card, new TokenCallback() {
            @Override
            public void onError(Exception error) {
                //Error
                Toast.makeText(getApplicationContext(), error.getLocalizedMessage(), Toast.LENGTH_LONG).show();
            }

            @Override
            public void onSuccess(Token token) {
                //send token to server
                Toast.makeText(getApplicationContext(), "Succesfully created a token", Toast.LENGTH_LONG).show();       // < this toast works, so my token is fine

                DatabaseTask databaseTask = new DatabaseTask("SENDTOKEN", token);
                databaseTask.execute();

            }
        });
    }

}

创建我的令牌后,我启动我的 DatabaseTask,然后它应该将该令牌发送到我的服务器(货币和金额在 PHP 脚本 atm 中硬编码)。该服务器有一个付费的 SSL 证书,我正在创建一个安全连接到我的 PHP 脚本,它应该处理其余的。

public class DatabaseTask extends AsyncTask<String, Void, String> {

    String command = "";
    Token token;

    public DatabaseTask(String mCommand, Token mToken){
        command = mCommand;
        token = mToken;
    }

    @Override
    protected String doInBackground(String... strings) {

        String echoData = "";

        if (command.equals("SENDTOKEN")) {
            try {
                URL url = new URL("https://.../StripeConnection.php"); //there is a connection between the code and PHP script. This is tested.
                HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();


                StringBuilder builder = new StringBuilder();
                builder.append(URLEncoder.encode("stripeToken", "UTF-8"));
                builder.append("=");
                builder.append(URLEncoder.encode(token.toString(), "UTF-8"));
                String urlParameters = builder.toString();

                connection.setRequestMethod("POST");
                connection.setDoOutput(true);
                DataOutputStream dStream = new DataOutputStream(connection.getOutputStream());

                dStream.writeBytes(urlParameters);
                dStream.flush();
                dStream.close();

                BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                String line = "";
                StringBuilder responseOutput = new StringBuilder();

                while ((line = br.readLine()) != null) {
                    Log.e("DatabaseTask", line);
                    responseOutput.append(line);
                }
                br.close();

                echoData = responseOutput.toString();


            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return echoData;
    }

    protected void onPostExecute(String mData){
        Log.e("DatabaseTask", "onPostExecute result: " + mData);
    }

此数据库任务适用的 PHP 脚本:

<?php

require_once('Stripe/init.php');

\Stripe\Stripe::setApiKey("sk_test_key");

$token = $_POST['stripeToken'];
$price = $_POST['price'];
$description = $_POST['description'];

// Create the charge on Stripe's servers - this will charge the user's card
try {
  $charge = Stripe\Charge::create(array(
    "amount" => 10000,          //"amount" => $price, in cents. //Hardcoded for testing
    "currency" => "cny",                        //Hardcoded for testing
    "source" => $token,                         //Hardcoded for testing
    "description" => "omschrijving" //"description" => $description //Hardcoded for testing
    ));

    echo "payment went succesfull";
} catch(\Stripe\Error\Card $e) {
  // The card has been declined
  echo "card was declined";
}

?>

似乎在我执行我的 DatabaseTask 和 PHP 脚本中的第一个 ECHO 之间的某处出错了,因为除了我的控制台中的一条巨大的 ERROR 消息之外,我再也没有得到任何回复。

08-23 16:04:28.083 20867-20867/... E/DatabaseTask: <br />
08-23 16:04:28.083 20867-20867/... E/DatabaseTask: <b>Fatal error</b>:  Uncaught exception 'Stripe\Error\InvalidRequest' with message 'No such token: &lt;com.stripe.android.model.Token@... id=&gt; JSON: { from API request 'req_...'
08-23 16:04:28.083 20867-20867/... E/DatabaseTask:   &quot;card&quot;: {
08-23 16:04:28.083 20867-20867/... E/DatabaseTask:     &quot;address_city&quot;: null,
08-23 16:04:28.083 20867-20867/... E/DatabaseTask:     &quot;address_country&quot;: null,
08-23 16:04:28.083 20867-20867/... E/DatabaseTask:     &quot;address_line1&quot;: null,
08-23 16:04:28.083 20867-20867/... E/DatabaseTask:     &quot;address_line2&quot;: null,
08-23 16:04:28.083 20867-20867/... E/DatabaseTask:     &quot;address_state&quot;: null,
08-23 16:04:28.083 20867-20867/... E/DatabaseTask:     &quot;address_zip&quot;: null,
08-23 16:04:28.083 20867-20867/... E/DatabaseTask:     &quot;country&quot;: &quot;US&quot;,
08-23 16:04:28.083 20867-20867/... E/DatabaseTask:     &quot;currency&quot;: null,
08-23 16:04:28.083 20867-20867/... E/DatabaseTask:     &quot;cvc&quot;: null,
08-23 16:04:28.083 20867-20867/... E/DatabaseTask:     &quot;exp_month&quot;: 12,
08-23 16:04:28.083 20867-20867/... E/DatabaseTask:     &quot;exp_year&quot;: 2019,
08-23 16:04:28.083 20867-20867/... E/DatabaseTask:     &quot;fingerprint&quot;: null,
08-23 16:04:28.083 20867-20867/... E/DatabaseTask:     &quot;last4&quot;: &quot;4242&quot;,
08-23 16:04:28.083 20867-20867/... E/DatabaseTask:     &quot;name&quot;: null,
08-23 16:04:28.083 20867-20867/... E/DatabaseTask:     &quot;number&quot;: null,
08-23 16:04:28.083 20867-20867/... E/DatabaseTask:     &quot;type&quot;: null
08-23 16:04:28.083 20867-20867/... E/DatabaseTask:   },
08-23 16:04:28.083 20867-20867/... E/DatabaseTask:   &quot;created&quot;: &quot;Aug 23, 2016 16:04:25&quot;,
08-23 16:04:28.083 20867-20867/... E/DatabaseTask:   &quot;id&quot;: &quot;tok_...&quot;,
08-23 16:04:28.083 20867-20867/... E/DatabaseTask:   &quot;livemode&quot;: false,
08-23 16:04:28.083 20867-20867/... E/DatabaseTask:   &quot;used&quot;: false
08-23 16:04:28.083 20867-20867/... E/DatabaseTask: }' in /home/.../Stripe/lib/ApiRequestor.php:108
08-23 16:04:28.083 20867-20867/... E/DatabaseTask: Stack trace:
08-23 16:04:28.083 20867-20867/... E/DatabaseTask: #0 /home...Stripe/lib/ApiRequestor.php(227): Stripe\ApiRequestor-&gt;handleApiError('{\n  &quot;error&quot;: {\n...', 400, Array, Array)
08-23 16:04:28.083 20867-20867/... E/DatabaseTask: #1 /home/.../Stripe/lib/ApiRequestor.php(65): Stripe\ApiRequestor-&gt;_interpretRe in <b>/home/.../Stripe/lib/ApiRequestor.php</b> on line <b>108</b><br />
08-23 16:04:28.093 20867-20867/... E/Checkout: <br /><b>Fatal error</b>:  Uncaught exception 'Stripe\Error\InvalidRequest' with message 'No such token: &lt;com.stripe.android.model.Token@... id=&gt; JSON: { from API request 'req_...'  &quot;card&quot;: {    &quot;address_city&quot;: null,    &quot;address_country&quot;: null,    &quot;address_line1&quot;: null,    &quot;address_line2&quot;: null,    &quot;address_state&quot;: null,    &quot;address_zip&quot;: null,    &quot;country&quot;: &quot;US&quot;,    &quot;currency&quot;: null,    &quot;cvc&quot;: null,    &quot;exp_month&quot;: 12,    &quot;exp_year&quot;: 2019,    &quot;fingerprint&quot;: null,    &quot;last4&quot;: &quot;4242&quot;,    &quot;name&quot;: null,    &quot;number&quot;: null,    &quot;type&quot;: null  },  &quot;created&quot;: &quot;Aug 23, 2016 16:04:25&quot;,  &quot;id&quot;: &quot;tok_...&quot;,  &quot;livemode&quot;: false,  &quot;used&quot;: false}' in /home/.../Stripe/lib/ApiRequestor.php:108Stack trace:#0 /home/.../Stripe/lib/ApiRequestor.php(227): Stripe\ApiRequestor-&gt;handleApiError('{\n  &quot;error&quot;: {\n...', 400, Array, Array)#1 /home/.../Stripe/lib/ApiRequestor.php(65): Stripe\ApiRequestor-&gt;_interpretRe in <b>/home/.../Stripe/lib/ApiRequestor.php</b> on line <b>108</b><br />

(链接、ID、令牌和其他个人资料被替换为...)

所以我已经尝试了几乎所有我能在网上找到的关于消息 "No such Token: " 的方法,但都无济于事。

我检查了什么: - 我的两把钥匙都没问题,这里没有错配 - 我的应用程序和我的 PHP 脚本之间有正确的连接 - 令牌已正确创建并由 Stripe 验证 - 我的服务器有一个付费的 SSL 证书,并与 HttpsURLConnection

建立了安全连接

我尝试了什么: - 一遍又一遍地刷新按键 - 在 POST 方法中将我的令牌作为字符串发送 - 尝试将我的令牌作为 JSONtoken 发送(从未成功)> Passing Stripe tokens to server and handling on server - 在 asyncTask 之外使用 POST 方法(读到它不需要) - 一遍又一遍地重新阅读 Stripe 的整个文档 > https://stripe.com/docs/mobile/android - 一遍又一遍地分析示例项目 > https://github.com/stripe/stripe-android/tree/master/example - 搜索视频教程,但只在网站上找到集成教程 - 搜索在线课程,再次仅用于网络集成

所以现在我真的很想知道 Stripe 对于这个看似不可能完成的任务意味着什么:

Set up an endpoint on your server that can receive an HTTP POST call for the token. In the onActivityResult method (for Android Pay) or the onSuccess callback (when using your own form), you’ll need to POST the supplied token to your server. Make sure any communication with your server is SSL secured to prevent eavesdropping.

如果您需要更多信息,请随时询问,因为这是完成我的第一个正式申请的最后一步。另外,请记住,我在过去 3 年里通过反复试验收集了我所有的知识,尽管我知道了很多东西,但我可能会遗漏一些基本知识。

谢谢。

此处的错误是,您没有传递令牌 ID tok_XXX,而是传递了从 Stripe 的 SDK 返回的整个令牌对象。然后请求失败并显示错误

Uncaught exception 'Stripe\Error\InvalidRequest' with message 'No such token: <com.stripe.android.model.Token@... id=> JSON: { from API request 'req_...'

您需要更改代码以将令牌 ID 发送到您的 PHP 脚本,这应该可以解决问题。

builder.append(URLEncoder.encode(token.getId(), "UTF-8"));