Code Monkey home page Code Monkey logo

wechatpay-php's Introduction

微信支付 WeChatPay OpenAPI SDK

[A]Sync Chainable WeChatPay v2&v3's OpenAPI SDK for PHP

GitHub actions Packagist Stars Packagist Downloads Packagist Version Packagist PHP Version Support Packagist License

概览

基于 Guzzle HTTP Client 的微信支付 PHP 开发库。

功能介绍

  1. 微信支付 APIv2 和 APIv3 的 Guzzle HTTP 客户端,支持 同步异步 发送请求,并自动进行请求签名和应答验签

  2. 链式实现的 URI Template

  3. 敏感信息加解密

  4. 回调通知的验签和解密

项目状态

当前版本为 1.4.9 测试版本。 项目版本遵循 语义化版本号。 如果你使用的版本 <=v1.3.2,升级前请参考 升级指南

环境要求

项目支持的环境如下:

  • Guzzle 7.0,PHP >= 7.2.5
  • Guzzle 6.5,PHP >= 7.1.2

我们推荐使用目前处于 Active Support 阶段的 PHP 8 和 Guzzle 7。

安装

推荐使用 PHP 包管理工具 Composer 安装 SDK:

composer require wechatpay/wechatpay

开始

ℹ️ 以下是 微信支付 API v3 的指引。如果你是 API v2 的使用者,请看 README_APIv2

概念

  • 商户 API 证书,是用来证实商户身份的。证书中包含商户号、证书序列号、证书有效期等信息,由证书授权机构(Certificate Authority ,简称 CA)签发,以防证书被伪造或篡改。详情见 什么是商户API证书?如何获取商户API证书?

  • 商户 API 私钥。你申请商户 API 证书时,会生成商户私钥,并保存在本地证书文件夹的文件 apiclient_key.pem 中。为了证明 API 请求是由你发送的,你应使用商户 API 私钥对请求进行签名。

⚠️ 不要把私钥文件暴露在公共场合,如上传到 Github,写在 App 代码中等。

  • 微信支付平台证书。微信支付平台证书是指:由微信支付负责申请,包含微信支付平台标识、公钥信息的证书。你需使用微信支付平台证书中的公钥验证 API 应答和回调通知的签名。

ℹ️ 你需要先手工 下载平台证书 才能使用 SDK 发起请求。

  • 证书序列号。每个证书都有一个由 CA 颁发的唯一编号,即证书序列号。

示例程序:微信支付平台证书下载

<?php

require_once('vendor/autoload.php');

use WeChatPay\Builder;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Util\PemUtil;

// 设置参数

// 商户号
$merchantId = '190000****';

// 从本地文件中加载「商户API私钥」,「商户API私钥」会用来生成请求的签名
$merchantPrivateKeyFilePath = 'file:///path/to/merchant/apiclient_key.pem';
$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);

// 「商户API证书」的「证书序列号」
$merchantCertificateSerial = '3775B6A45ACD588826D15E583A95F5DD********';

// 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名
$platformCertificateFilePath = 'file:///path/to/wechatpay/cert.pem';
$platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);

// 从「微信支付平台证书」中获取「证书序列号」
$platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);

// 构造一个 APIv3 客户端实例
$instance = Builder::factory([
    'mchid'      => $merchantId,
    'serial'     => $merchantCertificateSerial,
    'privateKey' => $merchantPrivateKeyInstance,
    'certs'      => [
        $platformCertificateSerial => $platformPublicKeyInstance,
    ],
]);

// 发送请求
$resp = $instance->chain('v3/certificates')->get(
    ['debug' => true] // 调试模式,https://docs.guzzlephp.org/en/stable/request-options.html#debug
);
echo $resp->getBody(), PHP_EOL;

文档

同步请求

使用客户端提供的 getputpostpatchdelete 方法发送同步请求。以 Native支付下单 为例。

try {
    $resp = $instance
    ->chain('v3/pay/transactions/native')
    ->post(['json' => [
        'mchid'        => '1900006XXX',
        'out_trade_no' => 'native12177525012014070332333',
        'appid'        => 'wxdace645e0bc2cXXX',
        'description'  => 'Image形象店-深圳腾大-QQ公仔',
        'notify_url'   => 'https://weixin.qq.com/',
        'amount'       => [
            'total'    => 1,
            'currency' => 'CNY'
        ],
    ]]);

    echo $resp->getStatusCode(), PHP_EOL;
    echo $resp->getBody(), PHP_EOL;
} catch (\Exception $e) {
    // 进行错误处理
    echo $e->getMessage(), PHP_EOL;
    if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
        $r = $e->getResponse();
        echo $r->getStatusCode() . ' ' . $r->getReasonPhrase(), PHP_EOL;
        echo $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL;
    }
    echo $e->getTraceAsString(), PHP_EOL;
}

请求成功后,你会获得一个 GuzzleHttp\Psr7\Response 的应答对象。 阅读 Guzzle 文档 Using Response 进一步了解如何访问应答内的信息。

异步请求

使用客户端提供的 getAsyncputAsyncpostAsyncpatchAsyncdeleteAsync 方法发送异步请求。以 退款 为例。

$promise = $instance
->chain('v3/refund/domestic/refunds')
->postAsync([
    'json' => [
        'transaction_id' => '1217752501201407033233368018',
        'out_refund_no'  => '1217752501201407033233368018',
        'amount'         => [
            'refund'   => 888,
            'total'    => 888,
            'currency' => 'CNY',
        ],
    ],
])
->then(static function($response) {
    // 正常逻辑回调处理
    echo $response->getBody(), PHP_EOL;
    return $response;
})
->otherwise(static function($e) {
    // 异常错误处理
    echo $e->getMessage(), PHP_EOL;
    if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
        $r = $e->getResponse();
        echo $r->getStatusCode() . ' ' . $r->getReasonPhrase(), PHP_EOL;
        echo $r->getBody(), PHP_EOL, PHP_EOL, PHP_EOL;
    }
    echo $e->getTraceAsString(), PHP_EOL;
});
// 同步等待
$promise->wait();

[get|post|put|patch|delete]Async 返回的是 Guzzle Promises。你可以做两件事:

  • 成功时使用 then() 处理得到的 Psr\Http\Message\ResponseInterface,(可选地)将它传给下一个 then()
  • 失败时使用 otherwise() 处理异常

最后使用 wait() 等待请求执行完成。

同步还是异步

对于大部分开发者,我们建议使用同步的模式,因为它更加易于理解。

如果你是具有异步编程基础的开发者,在某些连续调用 API 的场景,将多个操作通过 then() 流式串联起来会是一种优雅的实现方式。例如, 以函数链的形式流式下载交易帐单

链式 URI Template

URI Template 是表达 URI 中变量的一种方式。微信支付 API 使用这种方式表示 URL Path 中的单号或者 ID。

# 使用微信支付订单号查询订单
GET /v3/pay/transactions/id/{transaction_id}

# 使用商户订单号查询订单
GET /v3/pay/transactions/out-trade-no/{out_trade_no}

使用 链式 URI Template,你能像书写代码一样流畅地书写 URL,轻松地输入路径并传递 URL 参数。配置接口描述包后还能开启 IDE提示

链式串联的基本单元是 URI Path 中的 segmentssegments 之间以 -> 连接。连接的规则如下:

  • 普通 segment
    • 直接书写。例如 v3->pay->transactions->native
    • 使用 chain()。例如 chain('v3/pay/transactions/native')
  • 包含连字号(-)的 segment
    • 使用驼峰 camelCase 风格书写。例如 merchant-service 可写成 merchantService
    • 使用 {'foo-bar'} 方式书写。例如 {'merchant-service'}
  • Path 变量。URL 中的 Path 变量应使用这种写法,避免自行组装或者使用 chain(),导致大小写处理错误
    • 推荐使用 _variable_name_ 方式书写,支持 IDE 提示。例如 v3->pay->transactions->id->_transaction_id_
    • 使用 {'{variable_name}'} 方式书写。例如 v3->pay->transactions->id->{'{transaction_id}'}
  • 请求的 HTTP METHOD 作为链式最后的执行方法。例如 v3->pay->transactions->native->post([ ... ])
  • Path 变量的值,以同名参数传入执行方法
  • Query 参数,以名为 query 的参数传入执行方法

查询订单 GET 方法为例:

$promise = $instance
->v3->pay->transactions->id->_transaction_id_
->getAsync([
    // Query 参数
    'query' => ['mchid' => '1230000109'],
    // 变量名 => 变量值
    'transaction_id' => '1217752501201407033233368018',
]);

关闭订单 POST 方法为例:

$promise = $instance
->v3->pay->transactions->outTradeNo->_out_trade_no_->close
->postAsync([
    // 请求消息
    'json' => ['mchid' => '1230000109'],
    // 变量名 => 变量值
    'out_trade_no' => '1217752501201407033233368018',
]);

更多例子

视频文件上传

官方开发文档地址

// 参考上述指引说明,并引入 `MediaUtil` 正常初始化,无额外条件
use WeChatPay\Util\MediaUtil;
// 实例化一个媒体文件流,注意文件后缀名需符合接口要求
$media = new MediaUtil('/your/file/path/video.mp4');

$resp = $instance-
>chain('v3/merchant/media/video_upload')
->post([
    'body'    => $media->getStream(),
    'headers' => [
        'content-type' => $media->getContentType(),
    ]
]);

营销图片上传

官方开发文档地址

use WeChatPay\Util\MediaUtil;
$media = new MediaUtil('/your/file/path/image.jpg');
$resp = $instance
->v3->marketing->favor->media->imageUpload
->post([
    'body'    => $media->getStream(),
    'headers' => [
        'Content-Type' => $media->getContentType(),
    ]
]);

敏感信息加/解密

为了保证通信过程中敏感信息字段(如用户的住址、银行卡号、手机号码等)的机密性,

  • 微信支付要求加密上送的敏感信息
  • 微信支付会加密下行的敏感信息

下面以 特约商户进件 为例,演示如何进行 敏感信息加解密

use WeChatPay\Crypto\Rsa;
// 做一个匿名方法,供后续方便使用,$platformPublicKeyInstance 见初始化章节
$encryptor = static function(string $msg) use ($platformPublicKeyInstance): string {
    return Rsa::encrypt($msg, $platformPublicKeyInstance);
};

$resp = $instance
->chain('v3/applyment4sub/applyment/')
->post([
    'json' => [
        'business_code' => 'APL_98761234',
        'contact_info'  => [
            'contact_name'      => $encryptor('张三'),
            'contact_id_number' => $encryptor('110102YYMMDD888X'),
            'mobile_phone'      => $encryptor('13000000000'),
            'contact_email'     => $encryptor('[email protected]'),
        ],
        //...
    ],
    'headers' => [
        // $platformCertificateSerial 见初始化章节
        'Wechatpay-Serial' => $platformCertificateSerial,
    ],
]);

签名

你可以使用 Rsa::sign() 计算调起支付时所需参数签名。以 JSAPI支付 为例。

use WeChatPay\Formatter;
use WeChatPay\Crypto\Rsa;

$merchantPrivateKeyFilePath = 'file:///path/to/merchant/apiclient_key.pem';
$merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath);

$params = [
    'appId'     => 'wx8888888888888888',
    'timeStamp' => (string)Formatter::timestamp(),
    'nonceStr'  => Formatter::nonce(),
    'package'   => 'prepay_id=wx201410272009395522657a690389285100',
];
$params += ['paySign' => Rsa::sign(
    Formatter::joinedByLineFeed(...array_values($params)),
    $merchantPrivateKeyInstance
), 'signType' => 'RSA'];

echo json_encode($params);

回调通知

回调通知受限于开发者/商户所使用的WebServer有很大差异,这里只给出开发指导步骤,供参考实现。

  1. 从请求头部Headers,拿到Wechatpay-SignatureWechatpay-NonceWechatpay-TimestampWechatpay-SerialRequest-ID,商户侧Web解决方案可能有差异,请求头可能大小写不敏感,请根据自身应用来定;
  2. 获取请求body体的JSON纯文本;
  3. 检查通知消息头标记的Wechatpay-Timestamp偏移量是否在5分钟之内;
  4. 调用SDK内置方法,构造验签名串然后经Rsa::verfify验签;
  5. 消息体需要解密的,调用SDK内置方法解密;
  6. 如遇到问题,请拿Request-ID点击这里,联系官方在线技术支持;

样例代码如下:

use WeChatPay\Crypto\Rsa;
use WeChatPay\Crypto\AesGcm;
use WeChatPay\Formatter;

$inWechatpaySignature = '';// 请根据实际情况获取
$inWechatpayTimestamp = '';// 请根据实际情况获取
$inWechatpaySerial = '';// 请根据实际情况获取
$inWechatpayNonce = '';// 请根据实际情况获取
$inBody = '';// 请根据实际情况获取,例如: file_get_contents('php://input');

$apiv3Key = '';// 在商户平台上设置的APIv3密钥

// 根据通知的平台证书序列号,查询本地平台证书文件,
// 假定为 `/path/to/wechatpay/inWechatpaySerial.pem`
$platformPublicKeyInstance = Rsa::from('file:///path/to/wechatpay/inWechatpaySerial.pem', Rsa::KEY_TYPE_PUBLIC);

// 检查通知时间偏移量,允许5分钟之内的偏移
$timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
$verifiedStatus = Rsa::verify(
    // 构造验签名串
    Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
    $inWechatpaySignature,
    $platformPublicKeyInstance
);
if ($timeOffsetStatus && $verifiedStatus) {
    // 转换通知的JSON文本消息为PHP Array数组
    $inBodyArray = (array)json_decode($inBody, true);
    // 使用PHP7的数据解构语法,从Array中解构并赋值变量
    ['resource' => [
        'ciphertext'      => $ciphertext,
        'nonce'           => $nonce,
        'associated_data' => $aad
    ]] = $inBodyArray;
    // 加密文本消息解密
    $inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
    // 把解密后的文本转换为PHP Array数组
    $inBodyResourceArray = (array)json_decode($inBodyResource, true);
    // print_r($inBodyResourceArray);// 打印解密后的结果
}

异常处理

Guzzle 默认已提供基础中间件\GuzzleHttp\Middleware::httpErrors来处理异常,文档可见这里。 本SDK自v1.1对异常处理做了微调,各场景抛送出的异常如下:

  • HTTP网络错误,如网络连接超时、DNS解析失败等,送出\GuzzleHttp\Exception\RequestException
  • 服务器端返回了 5xx HTTP 状态码,送出\GuzzleHttp\Exception\ServerException;
  • 服务器端返回了 4xx HTTP 状态码,送出\GuzzleHttp\Exception\ClientException;
  • 服务器端返回了 30x HTTP 状态码,如超出SDK客户端重定向设置阈值,送出\GuzzleHttp\Exception\TooManyRedirectsException;
  • 服务器端返回了 20x HTTP 状态码,如SDK客户端逻辑处理失败,例如应答签名验证失败,送出\GuzzleHttp\Exception\RequestException
  • 请求签名准备阶段,HTTP请求未发生之前,如PHP环境异常、商户私钥异常等,送出\UnexpectedValueException;
  • 初始化时,如把商户证书序列号配置成平台证书序列号,送出\InvalidArgumentException;

以上示例代码,均含有catchotherwise错误处理场景示例,测试用例也覆盖了5xx/4xx/20x异常,开发者可参考这些代码逻辑进行错误处理。

定制

当默认的本地签名和验签方式不适合你的系统时,你可以通过实现signer或者verifier中间件来定制签名和验签,比如,你的系统把商户私钥集中存储,业务系统需通过远程调用进行签名。 以下示例用来演示如何替换SDK内置中间件,来实现远程请求签名结果验签,供商户参考实现。

use GuzzleHttp\Client;
use GuzzleHttp\Middleware;
use GuzzleHttp\Exception\RequestException;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

// 假设集中管理服务器接入点为内网`http://192.168.169.170:8080/`地址,并提供两个URI供签名及验签
// - `/wechatpay-merchant-request-signature` 为请求签名
// - `/wechatpay-response-merchant-validation` 为响应验签
$client = new Client(['base_uri' => 'http://192.168.169.170:8080/']);

// 请求参数签名,返回字符串形如`\WeChatPay\Formatter::authorization`返回的字符串
$remoteSigner = function (RequestInterface $request) use ($client, $merchantId): string {
    return (string)$client->post('/wechatpay-merchant-request-signature', ['json' => [
        'mchid' => $merchantId,
        'verb'  => $request->getMethod(),
        'uri'   => $request->getRequestTarget(),
        'body'  => (string)$request->getBody(),
    ]])->getBody();
};

// 返回结果验签,返回可以是4xx,5xx,与远程验签应用约定返回字符串'OK'为验签通过
$remoteVerifier = function (ResponseInterface $response) use ($client, $merchantId): string {
    [$nonce]     = $response->getHeader('Wechatpay-Nonce');
    [$serial]    = $response->getHeader('Wechatpay-Serial');
    [$signature] = $response->getHeader('Wechatpay-Signature');
    [$timestamp] = $response->getHeader('Wechatpay-Timestamp');
    return (string)$client->post('/wechatpay-response-merchant-validation', ['json' => [
        'mchid'     => $merchantId,
        'nonce'     => $nonce,
        'serial'    => $serial,
        'signature' => $signature,
        'timestamp' => $timestamp,
        'body'      => (string)$response->getBody(),
    ]])->getBody();
};

$stack = $instance->getDriver()->select()->getConfig('handler');
// 卸载SDK内置签名中间件
$stack->remove('signer');
// 注册内网远程请求签名中间件
$stack->before('prepare_body', Middleware::mapRequest(
    static function (RequestInterface $request) use ($remoteSigner): RequestInterface {
        return $request->withHeader('Authorization', $remoteSigner($request));
    }
), 'signer');
// 卸载SDK内置验签中间件
$stack->remove('verifier');
// 注册内网远程请求验签中间件
$stack->before('http_errors', static function (callable $handler) use ($remoteVerifier): callable {
    return static function (RequestInterface $request, array $options = []) use ($remoteVerifier, $handler) {
        return $handler($request, $options)->then(
            static function(ResponseInterface $response) use ($remoteVerifier, $request): ResponseInterface {
                $verified = '';
                try {
                    $verified = $remoteVerifier($response);
                } catch (\Throwable $exception) {}
                if ($verified === 'OK') { //远程验签约定,返回字符串`OK`作为验签通过
                    throw new RequestException('签名验签失败', $request, $response, $exception ?? null);
                }
                return $response;
            }
        );
    };
}, 'verifier');

// 链式/同步/异步请求APIv3即可,例如:
$instance->v3->certificates->getAsync()->then(static function($res) { return $res->getBody(); })->wait();

常见问题

如何下载平台证书?

使用内置的微信支付平台证书下载器

composer exec CertificateDownloader.php -- -k ${apiV3key} -m ${mchId} -f ${mchPrivateKeyFilePath} -s ${mchSerialNo} -o ${outputFilePath}

微信支付平台证书下载后,下载器会用获得的平台证书对返回的消息进行验签。下载器同时开启了 Guzzledebug => true 参数,方便查询请求/响应消息的基础调试信息。

ℹ️ 什么是APIv3密钥?如何设置?

证书和回调解密需要的AesGcm解密在哪里?

请参考AesGcm.php,例如内置的平台证书下载工具解密代码如下:

AesGcm::decrypt($cert->ciphertext, $apiv3Key, $cert->nonce, $cert->associated_data);

配合swoole使用时,上传文件接口报错

建议升级至swoole 4.6+,swoole在 4.6.0 中增加了native-curl(swoole/swoole-src#3863)支持,我们测试能正常使用了。 更详细的信息,请参考#36

如何加载公/私钥和证书

v1.2提供了统一的加载函数 Rsa::from($thing, $type)

  • Rsa::from($thing, $type) 支持从文件/字符串加载公/私钥和证书,使用方法可参考 RsaTest.php
  • Rsa::fromPkcs1是个语法糖,支持加载 PKCS#1 格式的公/私钥,入参是 base64 字符串
  • Rsa::fromPkcs8是个语法糖,支持加载 PKCS#8 格式的私钥,入参是 base64 字符串
  • Rsa::fromSpki是个语法糖,支持加载 SPKI 格式的公钥,入参是 base64 字符串
  • Rsa::pkcs1ToSpki是个 RSA公钥 格式转换函数,入参是 base64 字符串

如何计算商家券发券 API 的签名

使用 Hash::sign()计算 APIv2 的签名,示例请参考 APIv2 文档的 数据签名

为什么 URL 上的变量 OpenID,请求时被替换成小写了?

本 SDK 把 URL 中的大写视为包含连字号的 segment。请求时, camelCase 会替换为 camel-case。相关 issue 可参考 #56#69

为了避免大小写错乱,URL 中存在变量时的正确做法是:使用 链式 URI Template 的 Path 变量。比如:

  • 推荐写法 ->v3->marketing->favor->users->_openid_->coupons->post(['openid' => 'AbcdEF12345'])
  • ->v3->marketing->favor->users->{'{openid}'}->coupons->post(['openid' => 'AbcdEF12345'])
  • ->chain('{+myurl}')->post(['myurl' => 'v3/marketing/favor/users/AbcdEF12345/coupons'])
  • ->{'{+myurl}'}->post(['myurl' => 'v3/marketing/favor/users/AbcdEF12345/coupons'])

联系我们

如果你发现了BUG或者有任何疑问、建议,请通过issue进行反馈。

也欢迎访问我们的开发者社区

链接

License

Apache-2.0 License

wechatpay-php's People

Contributors

canvaskent avatar codezm avatar redjard avatar thenorthmemory avatar tpirc3 avatar xy-peng avatar yurunsoft avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wechatpay-php's Issues

企业付款到零钱问题

用V2的企业付款到零钱功能,会出现偶尔重复付款多次的情况,可能是异步方式,多次请求了

创建支付分订单API 报错,乱码

Client error: POST https://api.mch.weixin.qq.com/v3/payscore/serviceorder resulted in a 400 Bad Request response:
{"code":"PARAM_ERROR","message":"µ£ìÕèíÕ╝ÇÕºïµùÂÚù┤õ©ìÞ⢵ù®õ║ÄÞ░âþö¿µÄÑÕÅúµùÂÚù┤´╝îµêûõ╝áÕàÑÕø║Õ«ÜÕÇ╝OnAcceptÞí¿þñ║þö¿µ (truncated...)

请问这个是什么原因

Builder::factory 参数和支付类型问题

'certs' => [ // 可由内置的平台证书下载器 ./bin/CertificateDownloader.php 生成 'YYYYYYYYYY' => PemUtil::loadCertificate('/path/to/wechatpay/cert.pem') ],

这里的Y是个什么?我填入序列号报错,不填没错。
还有个问题
支付只有native类型?

标题写错了

image
红色框起来的标题的应该是商户进件提交申请单API

商户API证书序列号

WeChatPay\Exception\InvalidArgumentException: The certs(xxxxxxxxx) contains the merchant's certificate serial number(xxxxxxxx) which is not allowed here. in file /www/wwwroot/xxx/vendor/wechatpay/wechatpay/src/ClientJsonTrait.php on line 190
按照管理后台进行配置的,查询看见后台有一个关闭的issues里问过;怕老师看不见再一下;
请问商户API证书序列号要怎么配置?

php 使用 swoole报错。

curl-native 已经确认开启了。

swoole

Swoole => enabled
Author => Swoole Team [email protected]
Version => 4.8.9
Built => Apr 19 2022 22:22:56
coroutine => enabled with boost asm context
epoll => enabled
eventfd => enabled
signalfd => enabled
cpu_affinity => enabled
spinlock => enabled
rwlock => enabled
sockets => enabled
openssl => OpenSSL 1.0.2u 20 Dec 2019
curl-native => enabled
pcre => enabled
zlib => 1.2.7
mutex_timedlock => enabled
pthread_barrier => enabled
futex => enabled
async_redis => enabled

Directive => Local Value => Master Value
swoole.enable_coroutine => On => On
swoole.enable_library => On => On
swoole.enable_preemptive_scheduler => Off => Off
swoole.display_errors => On => On
swoole.use_shortname => On => On
swoole.unixsock_buffer_size => 8388608 => 8388608

调用上传媒体素材报
WARNING ProcessPool::wait(): worker#3 abnormal exit, status=0, signal=11
....

GCC_VERSION: 4.8.5 20150623 (Red Hat 4.8.5-44)
OPENSSL_VERSION: OpenSSL 1.0.2u 20 Dec 2019
PHP_VERSION : 7.4.28

建议SDK最低支持PHP7.0版本

建议SDK最低支持PHP7.0版本。毕竟SDK要适配大众环境,而不是大众环境为了SDK升级已经部署的PHP环境。

v2 企业付款到零钱 报错

按README里面例子使用 业付款到零钱接口,报错以下错误

Argument 2 passed to WeChatPay\\Transformer::content() must be of the type string, integer given, called in xxx\vendor\\wechatpay\\wechatpay\\src\\Transformer.php on line 156"

v3多个商户号解密支付回调

image

支付回调与下单API没有相关id,在多个商户号的情况下如何识别回调属于哪个订单?

v2的回调是带有transaction_id,所以可以找到对应API密钥
image

目前想到的方法就只能loop 所有APIv3密钥尝试解密。

传递正常的参数,提示参数类型格式错误

php错误提示很明显,参数格式有问题,但是知道具体那个参数问题?

json数据

{"organization_type":"2401","id_card_info":{"id_card_copy":"4AwYFk-PxXLINImzbD0Gmv5zOL7V3d_bfT6zQeXUc5L88hvsMYfOqybCZhzTHS4VdBWt_chQndK4PXugzXLOaEUhDrmhr7ISmvmGRlewzS0","id_card_national":"4AwYFk-PxXLINImzbD0GmjXLkUAjf1WjOchmdltYk0oef0yO_DCtRplp8nZnf2NRvWvcRJLkI1jN8MK4A762IWe-mC7osKVI6gb2btRvz_k","id_card_name":"lTTvIKywa8Ksm+ntTyMtgfawrqoKUhVDDRrBZXgbV596X9XWgbu7qstmrMbdmQh6Wm3rzrhfDJISjzFLivFGSK544qwblV8zrm7z/q2//KNn3v+9uQh4gnrHb1p2kGoOz/L5J3t7wkkvf9JTlWTFDmTXJcpqcTV1UyAL2j5Ad3XKa/h8c2H3cB0sAkmvZU2cbACbitgTn50v7ya16ZiqISnP6MDJEgcJiLXrrPGa5tATPFfn3NmSDRgMvZxTlwhOeR7TSX1/gPLinAR+pnkfbDmsvM3yebCehIrvmatxt6gnYEiljzZZ0mJJZWFvh4GRl7XpFlIdMIQcVRM4Vza8tA==","id_card_number":"N8zqykRXema++232pXNgb3S2ImddBj+7QXI+pMPYI8jHbHEkDdiXQcsvyJMUotxYXg7+1zK2U7uSvaDMR1ORvj33pXOSrp4B5kUzIdMjZb7L1nFiygsGeHWFJp5V2RfJ9nXXbKj/82JdhMb4sUsv1LTjgx2UzIG4qbwWQSFGeLjRKT86wRsKrNb86eF2Xvu8LMZuw/AyGGfNEEVzRuHfDtmxq7hreuJztEXDTu+Qxtyl9X1+jovGeS6TcWziGm92G2QllarsCXSfpzfAnSPNx4uDka+JTZDE1FNX8S8ujrmowaDq2NAe/zyWfqLg1JNIK2fWdsoQUCiUWcf/QZATNw==","id_card_valid_time":"2036-03-13"},"contact_info":{"contact_type":"65","contact_name":"lVOHUP2Oc2JwKk3JjYQwE/G9l58bE7BilcgjZ3tLEyB07IotvLQ42DLj/q3PkaQtEF/HyGP6AAfPTdhOpCKW1X2rqjbO4JMTXhj75yNtUWgnBqMQ/+esgzh6gIBITudMhT7drsggqj4pBsEkg1msLaJBju/mYxRFrWcghwpNg2me9oMoNI28j1my9TDiivItKOhT+YRntwopLQb8qtqnoUMNFwpvnXuipjjj7I24flawmwKr+eD7wPYO/cc341PZV/dhcdZ8yjAl+HS3HqvZ76zfC+RyozOzk8trGfqnQNebjRedalW5q8vbnmEV60z6cjgwIHzVBSHqO7eS9h2QRA==","contact_id_card_number":"JzKxKbgzgtMfQVmSSkJ8hcjqXJXnBZBJv0ji0kMr3atNLhciy8U/XhgQYamZve3/92Az98mg0MmJ+9xuyzkORTneLY6THKAbBpl+UMu7I4Mc/nCcYlntv6n3kuRywvWB0FMYIADrdTElsGRgh7iUeo3qlKRFH/2jJT5mlNkguC6TAt0ULJyMLVycxoEL5ggKzvMtt6AN4AYJWLmZJY7ZEkhS86z99zjuXdrUTikGfFBhTimK1O4/kGV9SOGzJguajVZaPIOU4WmGOuALpCEFHQoIz1R+N7ZFqk1L3cvdtfxxtfIBGleXl91CKm8BdAXcNVonhwFDodRQWr4QUWqRig==","mobile_phone":"eQa4SIQ2XJW68Kya4AFg5RNrLvwZf9hJCBerx99LZL0KxBjqZb1nP0l8JtWe8mzSJZgYM9i7qRWdhbtzXm8I5fxU8bCmBZX0ODyy6FAKPSacDtfQLLHpGW5Q/j07gimx5hq5n4x/vLbr3i3yKTOm1lbCeiDOOltDxAhnRoHI6pu61BBNwrD7Y8k9CKwTWCccRcGLJBnD9Wj/RYu2c2MFGMZitw/kCJk4fyuSzTC6vMMb2qpCMvWljgTn3LgCe4YX6ukJm2vf4C684XzsKL8qCzgMM4lj7QqTEVakU7uqonAuPnu4G4mzhlmcBiG4U80FILrv0UYjmuV9FxlJWVj5iQ=="},"need_account_info":false,"sales_scene_info":{"store_name":"测试店铺信息","store_url":"http://test.xxx.com/test.php"},"merchant_shortname":"大红料","out_request_no":"20210721171352"}

报错信息

image

页面代码

image

输入请求参数xml格式错误

输入请求参数xml格式错误?

请求网址 https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey

$url = "https://api.mch.weixin.qq.com/sandboxnew/pay/getsignkey";
$nonce_str = "5K8264ILTKCH16CQ2502SI8ZNMTM67VS";
$options = [
    'form_params' =>
        'xml'   => [
              'mch_id' => "123456",
              'nonce_str' => $nonce_str,
              'sign' => $nonce_str
          ]
];
$client = new \GuzzleHttp\Client();
$response = $client->request('POST', $url, $options);
$stream   = \GuzzleHttp\Psr7\Utils::streamFor($response->getBody());
$content  = $stream->getContents();
dump($content);

输出内容

<xml>
  <return_code><![CDATA[FAIL]]></return_code>
  <retmsg><![CDATA[输入请求参数xml格式错误]]></retmsg>
  <retcode><![CDATA[1]]></retcode>
</xml>

请问这是什么原因?

apiv2接口SIGN_ERROR错误

请求前的参数:
Array
(
[xml] => Array
(
[mch_billno] => etGkDmT3BJyuhnhU9d
[mch_id] => xxxxx
[wxappid] => wx01111111
[send_name] => aaaaa
[re_openid] => o8xSOxxxxxxx
[total_amount] => 100
[total_num] => 1
[wishing] => 红包祝福语
[client_ip] => 192.168.0.1
[act_name] => 活动名称
[remark] => 备注
)
[security] => 1
)

apiv2密钥尝试更换过,更换后还是返回签名错误,请问我要怎样处理?
请求代码:$instance->v2->mmpaymkttransfers->sendredpack->postAsync($data)

查询子商户模式支付的订单时,抛错“请求中含有未在API文档中定义的参数”,请问是什么原因呢?

查询用的V3的接口“->v3->pay->transactions->id->{'{transaction_id}'}”,拼接后的请求路径为: “https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/xxxx?mchid=xxxx&sub_mch_id=xxxx”,具体抛错信息如下:
400 Bad Request
{"code":"PARAM_ERROR","detail":{"location":null,"value":["/query/sub_mch_id"]},"message":"请求中含有未在API文档中定义的参数"}

付款到零钱,走的otherwise,返回空,无法确认交易结果

问题:
1.交易能成功:收到转账、商户后台也能看见交易信息,但是接口直接走了otherwise且返回了空数组,导致无法确认交易结果
2.交易查询,同样走了otherwise,拿不到交易结果
3.此问题已同技术客服确认
image

代码信息:
1.调用sdk
image
2.输出结果
image

JSAPI下单 请求成功但是body为空

$instance->v3->pay
->transactions->jsapi->post(
['json'=>[
'appid'=>$app_id,
'mchid'=>$mchid,
'description'=>$order_name,
'out_trade_no'=>$out_trade_no,
'notify_url'=>'test.com',
'amount'=>[
'total'=>(int) bcdiv( $order->good->price,100),
],
'payer'=>[
'openid'=>$order->user->open_id,
],
]]
)

(参数是正确的防止泄密像回调地址在这里是随便写的)
当我使用如上代码发起请求时,请求是通的,但是返回的body是空的这是为什么

服务商相关 factory 构建 instance

请问 服务商也是这样么 [‘mchid’=>服务商sp_mchid, ‘serial ’=> 服务商证书序列号, ‘privatKey ’=>服务商私钥,certs[平台证书序列号=> #平台证书内容] ] 这样么

证书下载错误

  • Trying 14.215.140.116...
  • TCP_NODELAY set
  • Expire in 74995 ms for 3 (transfer 0x1ff75985080)
  • Expire in 200 ms for 4 (transfer 0x1ff75985080)
  • Connected to api.mch.weixin.qq.com (14.215.140.116) port 443 (#0)
  • ALPN, offering http/1.1
  • SSL certificate problem: unable to get local issuer certificate
  • Closing connection 0
    cURL error 60: (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) for https://api.mch.weixin.qq.com/v3/certificates
    #0 D:\phpstudy_pro\WWW\video_project\vendor\guzzlehttp\guzzle\src\Handler\CurlFactory.php(158): GuzzleHttp\Handler\CurlFactory::createRejection(Object(GuzzleHttp\Handler\EasyHandle
    ), Array)
    #1 D:\phpstudy_pro\WWW\video_project\vendor\guzzlehttp\guzzle\src\Handler\CurlFactory.php(110): GuzzleHttp\Handler\CurlFactory::finishError(Object(GuzzleHttp\Handler\CurlMultiHandl
    er), Object(GuzzleHttp\Handler\EasyHandle), Object(GuzzleHttp\Handler\CurlFactory))
    #2 D:\phpstudy_pro\WWW\video_project\vendor\guzzlehttp\guzzle\src\Handler\CurlMultiHandler.php(236): GuzzleHttp\Handler\CurlFactory::finish(Object(GuzzleHttp\Handler\CurlMultiHandl
    er), Object(GuzzleHttp\Handler\EasyHandle), Object(GuzzleHttp\Handler\CurlFactory))
    #3 D:\phpstudy_pro\WWW\video_project\vendor\guzzlehttp\guzzle\src\Handler\CurlMultiHandler.php(168): GuzzleHttp\Handler\CurlMultiHandler->processMessages()
    #4 D:\phpstudy_pro\WWW\video_project\vendor\guzzlehttp\guzzle\src\Handler\CurlMultiHandler.php(183): GuzzleHttp\Handler\CurlMultiHandler->tick()
    #5 D:\phpstudy_pro\WWW\video_project\vendor\guzzlehttp\promises\src\Promise.php(248): GuzzleHttp\Handler\CurlMultiHandler->execute(true)
    #6 D:\phpstudy_pro\WWW\video_project\vendor\guzzlehttp\promises\src\Promise.php(224): GuzzleHttp\Promise\Promise->invokeWaitFn()
    #7 D:\phpstudy_pro\WWW\video_project\vendor\guzzlehttp\promises\src\Promise.php(269): GuzzleHttp\Promise\Promise->waitIfPending()
    #8 D:\phpstudy_pro\WWW\video_project\vendor\guzzlehttp\promises\src\Promise.php(226): GuzzleHttp\Promise\Promise->invokeWaitList()
    #9 D:\phpstudy_pro\WWW\video_project\vendor\guzzlehttp\promises\src\Promise.php(62): GuzzleHttp\Promise\Promise->waitIfPending()
    #10 D:\phpstudy_pro\WWW\video_project\vendor\wechatpay\wechatpay\bin\CertificateDownloader.php(109): GuzzleHttp\Promise\Promise->wait()
    #11 D:\phpstudy_pro\WWW\video_project\vendor\wechatpay\wechatpay\bin\CertificateDownloader.php(47): CertificateDownloader->job(Array)
    #12 D:\phpstudy_pro\WWW\video_project\vendor\wechatpay\wechatpay\bin\CertificateDownloader.php(235): CertificateDownloader->run()
    #13 {main}

当金额为1.1元时,jsapi下单报错

这个接口 $resp = $instance->chain('v3/pay/transactions/jsapi')->post(['json' => [ ], ]);

Client error: POST https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi resulted in a 400 Bad Request response:
{"code":"PARAM_ERROR","detail":{"location":"body","value":110.00000000000001},"message":"无法将 JSON 输入源“/bod (truncated...)

APIv3回调通知 样例代码 中的错误?

if ($timeOffsetStatus && $verifiedStatus) {
    // 这个变量用来干什么的?
    $inBodyArray = (array)json_decode($inBody, true);
    // 这段代码是什么情况????
    ['resource' => [
        'ciphertext'      => $ciphertext,
        'nonce'           => $nonce,
        'associated_data' => $aad
    ]] = $inBodyArray;
    $inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
    $inBodyResourceArray = (array)json_decode($inBodyResource, true);
    // print_r($inBodyResourceArray);// 打印解密后的结果
}

关于v2接口中间件强行要求验证sign字段问题

运行环境

php:7.4
wechatpay-php:1.4.2

描述你的问题现象

在使用此组件对接了大部分v3接口后,有些业务依然需要对接v2接口,比如“付款到零钱”接口。(https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2)

这类接口的响应数据中,并没有sign字段,但ClientXmlTrait.php中设置了一个guzzlehttp的中间件方法static::transformResponse(),该方法必须验证body中的sign字段,当验证不通过或者字段不存在,此时会抛出一个RejectionException的异常。这导致了,即使接口返回业务正常响应(例如已经真实付款到用户零钱),依然抛这个错。目前唯一可以解决的方法,是在RejectionException的抛错中通过getReason()方法来找到返回业务正常的响应。

提交了一个修改建议
#91

php7.2.1无法使用?

使用的是7.2.1的版本,composer报错如下:

Your requirements could not be resolved to an installable set of packages.

Problem 1
- wechatpay/wechatpay 1.0.2 requires guzzlehttp/guzzle ^7.0 -> satisfiable by guzzlehttp/guzzle[7.0.0, 7.0.1, 7.1.0, 7.1.1, 7.2.0, 7.3.0].
- wechatpay/wechatpay 1.0.3 requires guzzlehttp/guzzle ^7.0 -> satisfiable by guzzlehttp/guzzle[7.0.0, 7.0.1, 7.1.0, 7.1.1, 7.2.0, 7.3.0].
- wechatpay/wechatpay 1.0.1 requires php ^7.2.5 || ^8.0 -> your PHP version (7.2.1) does not satisfy that requirement.
- wechatpay/wechatpay 1.0.0 requires php ^7.2.5 || ^8.0 -> your PHP version (7.2.1) does not satisfy that requirement.
- guzzlehttp/guzzle 7.3.0 requires php ^7.2.5 || ^8.0 -> your PHP version (7.2.1) does not satisfy that requirement.
- guzzlehttp/guzzle 7.2.0 requires php ^7.2.5 || ^8.0 -> your PHP version (7.2.1) does not satisfy that requirement.
- guzzlehttp/guzzle 7.1.1 requires php ^7.2.5 -> your PHP version (7.2.1) does not satisfy that requirement.
- guzzlehttp/guzzle 7.1.0 requires php ^7.2.5 -> your PHP version (7.2.1) does not satisfy that requirement.
- guzzlehttp/guzzle 7.0.1 requires php ^7.2.5 -> your PHP version (7.2.1) does not satisfy that requirement.
- guzzlehttp/guzzle 7.0.0 requires php ^7.2.5 -> your PHP version (7.2.1) does not satisfy that requirement.
- Installation request for wechatpay/wechatpay ^1.0 -> satisfiable by wechatpay/wechatpay[1.0.0, 1.0.1, 1.0.2, 1.0.3].

【官方调查问卷】微信支付 API v3 PHP SDK 开发者体验调查

为了向广大开发者提供更好的使用体验,微信支付诚挚邀请您将使用微信支付 API v3 SDK中的感受反馈给我们。本问卷可能会占用您不超过2分钟的时间,感谢您的支持。

问卷系统使用的腾讯问卷,您可以点击这里,或者扫描以下二维码参与调查。

image

Call to undefined method think\\exception\\ErrorException::getResponse()

->then(function($response) {
// 正常逻辑回调处理
echo $response->getBody()->getContents();
return $response;
})
->otherwise(function($exception) {
// 异常错误处理
$body = $exception->getResponse()->getBody();
echo $body->getContents();
// echo $exception->getTraceAsString(), PHP_EOL;
})

每次都进入otherwise

提示Call to undefined method think\exception\ErrorException::getResponse()

demo 微信支付平台证书下载 提示 SIGN_ERROR

运行环境

- OS:ubuntu
- PHP:7.4
- wechatpay-php:1.4

描述你的问题现象

{"success":false,"code":500,"message":"Client error: GET https:\/\/api.mch.weixin.qq.com\/v3\/certificates resulted in a 401 Unauthorized response:\n{"code":"SIGN_ERROR","message":" 签名信息错误,验签失败"}\n"}

image

php 框架是 hyperf 2.2

现金红包V2接口证书提示错误

证书在v3接口使用正常,在v2的现金红包接口错误码:CA_ERROR,证书出错,请登录微信支付商户平台下载证书
请问证书有效期没有失效,为什么会提示这个错误?应该怎样解决?

对APIv3强制验签逻辑进行优化,对已知无签返回的请求,自动忽略验签

运行环境

- wechatpay-php: 1.4.4

描述你的问题现象

需求

引申自 #92 , 当前已知在 APIv3 的三个接口上,有三种“特殊”逻辑,当前版本SDK并没有特殊处理,如下:

1. 国内商户客诉图片下载

URL: /v3/merchant-service/images/{slot}

接口本身返回的流stream,无验签逻辑;

2. 国内商户账单下载

URL: /v3/billdownload/file

接口本身返回的流stream,验签逻辑依赖前置请求账单的SHA1摘要,而前置接口的返回的SHA1值,已按标准RSA公钥逻辑验签;

3. 海外商户账单下载

URL: /hk/v3/statements

请求的返回值,签名含两种逻辑,标准RSA公钥验签逻辑注1及流stream SHA1摘要逻辑;

注1: 验签的是 SHA1 摘要的JSON表达式,而非流,与标准RSA公钥验签验证返回BODY不同;

分析

  1. 第1种情形,接口设计的比较另类另说(见 #85 ),无验签逻辑需要客户端自动忽略;

  2. 第2种是两步请求分开验签,先RSASHA1,验签逻辑含上下文context语境,后置的客户端本地验签逻辑可循 以函数链的形式流式下载交易帐单 示例,在 ->then 链上处理,当前请求返回值无明确签名,客户端需要忽略验签;

  3. 第3种情形可以看作是第2种的特例,一步请求,接口即回吐了RSA签名又包含SHA1摘要,客户端设计可以仿第2种逻辑,仅对RSA签名部分进行验签,保证当前请求的返回值,是来自平台RSA私钥数据签名注2SHA1摘要逻辑交由客户端本地在 ->then 链上自行比对摘要值;

注2: 按照规范,非对称加密的公钥是公开的,可任意发行的,而平台RSA私钥是私密数据,一次请求即含签名又含摘要,虽然任何人(持有效公钥)都可以验签,但RSA签名值本身是不可伪造,是安全的。

改进

按照上述分析,对前两种接口设计,当次请求/响应需要忽略RSA验签;对第三种逻辑,需要构造一个本地摘要值JSON串,做一次RSA公钥验签,验签若失败,则按当前处理逻辑抛\GuzzleHttp\Exception\RequestException异常;RSA验签成功则(忽略SHA1值比对逻辑)放行。

只是说这个sdk 微信开发sdk的人是**没人会反驳吧

例子就行下面这个例子

获取平台证书需要提供平台证书序列号和平台证书 不提供就报错 问? 老子不是在获取么 你都不给获取的机会就开始报错 这不是sb是什么

// 从本地文件中加载「微信支付平台证书」,用来验证微信支付应答的签名
$platformCertificateFilePath = 'file:///path/to/wechatpay/cert.pem';
$platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);

// 从「微信支付平台证书」中获取「证书序列号」
$platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);

// 构造一个 APIv3 客户端实例
$instance = Builder::factory([
    'mchid'      => $merchantId,
    'serial'     => $merchantCertificateSerial,
    'privateKey' => $merchantPrivateKeyInstance,
    'certs'      => [
        $platformCertificateSerial => $platformPublicKeyInstance,
    ],
]);

// 发送请求
$resp = $instance->chain('v3/certificates')->get(
    ['debug' => true] // 调试模式,https://docs.guzzlephp.org/en/stable/request-options.html#debug
);

BuilderChainable chain 会把大写转化成小写的问题

调用 BuilderChainable chain 会把大写转化成小写,例如查询订单需要拼上订单号,单订单号有大写的时候,会被转成小写
例如 N123123 会被转成功 -n123123
image

使用链式的凡事也会被转化
$resp = $this->instance->v3->pay->partner->transactions->outTradeNo->{$out_trade_no}

输出的是 n123123 不存在

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.