为了保证支付接口使用的安全,微信支付平台在支付API中使用了一些用于接口安全调用的技术。在调用时接口需要使用商户私钥进行接口调用的签名,获取到微信支付平台的应答之后也需要对应答进行签名验证。微信的应答签名使用平台证书来进行签名验证,因此在调用支付接口前还需要实现平台证书的下载以及管理。另外微信支付在回调通知和平台证书下载接口中,对关键信息进行了AES-256-GCM加密,因此开发者还需要了解如何使用APIv3密钥进行数据解密。在调用具体接口之前需要了解这是逻辑,并实现接口调用的一些基础代码。
商户接入微信支付,调用API必须遵循以下规则:
1)微信支付API v3使用 JSON 作为消息体的数据交换格式。请求须设置HTTP头部:
微信支付使用APIv3密钥对请求进行签名。微信支付会在收到请求后进行签名的验证。如果签名验证不通过,微信支付将会拒绝处理请求,并返回401 Unauthorized。
开发人员调用支付接口时需要按照以下的规则构造签名串。签名串一共有五行,每一行为一个参数,行尾以 \n结束,包括最后一行。
HTTP请求方法\n
URL\n
请求时间戳\n
请求随机串\n
请求报文主体\n
然后使用商户私钥对待签名串进行SHA256 with RSA签名,并对签名结果进行Base64编码得到签名值。
微信支付要求请求使用HTTP Authorization头来传递签名。Authorization由认证类型和签名信息两个部分组成。具体内容为:
type MchWxapp struct {
//商户类型 0直连商户 1服务商商户
MchType int
//商户对应的appid
Appid string
//商户号
Mchid string
//商户的API v3密钥
MchAPIKey string
//商户API私钥
MchPrivateKey *rsa.PrivateKey
//商户 API 证书
MchCertificate *x509.Certificate
}
商户的API私钥用于生成调用签名,接下来给出商户密钥的加载代码:
func LoadPrivateKeyWithPath(path string) (privateKey *rsa.PrivateKey, err error) {
privateKeyBytes, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
block, _ := pem.Decode([]byte(privateKeyStr))
if block == nil {
return nil, fmt.Errorf("decode private key err")
}
key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
privateKey, ok := key.(*rsa.PrivateKey)
if !ok {
return nil, fmt.Errorf("%s is not rsa private key", privateKeyStr)
}
return privateKey, nil
}
生成请求头Authorization信息时需要用到商户证书的SerialNumber,以下是商户证书的加载代码:
func LoadCertificateWithPath(path string) (certificate *x509.Certificate, err error) {
certificateBytes, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
block, _ := pem.Decode([]byte(certificateStr))
if block == nil {
return nil, fmt.Errorf("decode certificate err")
}
certificate, err = x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, err
}
}
接下来要进行签名串的构造以及对签名串进行签名,具体代码如下:
func GenerateWxPayReqHeader(ctx *MchParam, method string, rawUrl string, signBody string) (authorization string, err error){
timestamp := time.Now().Unix()
url, err := url.Parse(rawUrl)
if err != nil {
return "", err
}
nonce, err := GenerateNonce()
if err != nil {
return "", err
}
SignatureMessageFormat := "%s\n%s\n%d\n%s\n%s\n"
message := fmt.Sprintf(SignatureMessageFormat, method, url.RequestURI(), timestamp, nonce, signBody)
signatureResult, err := SignSHA256WithRSA(ctx.MchPrivateKey, message)
if err != nil {
return "", err
}
certSerialNo := fmt.Sprintf("%X", ctx.MchCertificate.SerialNumber)
HeaderAuthorizationFormat := "WECHATPAY2-SHA256-RSA2048 mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\""
authorization = fmt.Sprintf(HeaderAuthorizationFormat, ctx.Mchid, nonce, timestamp, certSerialNo, signatureResult)
return authorization, nil
}
代码中使用GenerateNonce()生成一个32个字节的请求随机串,并调用SignSHA256WithRSA对待签名串进行SHA256 with RSA签名。下面是函数SignSHA256WithRSA的实现:
func SignSHA256WithRSA(privateKey *rsa.PrivateKey, source string)
(signature string, err error) {
h := crypto.Hash.New(crypto.SHA256)
_, err = h.Write([]byte(source))
if err != nil {
return "", nil
}
hashed := h.Sum(nil)
signatureByte, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(signatureByte), nil
}
最后来我们通过代码来看看如何通过HTTP的POST方法来调用支付接口。以下代码中去掉了响应数据签名验证的逻辑,响应数据签名验证稍后再来分析:
func WxPayPostV3(ctx *MchParam, url string, data []byte) (string, error) {
token, err := GenerateWxPayReqHeader(ctx, http.MethodPost, url, string(data))
if err != nil {
log.Println(err)
return "", err
}
request, err := http.NewRequest("POST", url, bytes.NewBuffer(data))
if err != nil {
return "", err
}
request.Header.Add("Authorization", token)
request.Header.Add("User-Agent", "go pay sdk")
request.Header.Add("Content-type", "application/json;charset='utf-8'")
request.Header.Add("Accept", "application/json")
client := &http.Client{Timeout: 5 * time.Second}
resp, err := client.Do(request)
if err != nil {
log.Println(err)
return "", err
}
defer resp.Body.Close()
result, _ := ioutil.ReadAll(resp.Body)
if resp.StatusCode != 200 && resp.StatusCode != 204 {
err := fmt.Errorf("status:%d;msg=%s", resp.StatusCode, string(result))
log.Println(err)
return string(result), err
}
return string(result), nil
}
我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0
Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack
我想用ruby编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r
刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr
在我的Controller中,我通过以下方式在我的index方法中支持HTML和JSON:respond_todo|format|format.htmlformat.json{renderjson:@user}end在浏览器中拉起它时,它会自然地以HTML呈现。但是,当我对/user资源进行内容类型为application/json的curl调用时(因为它是索引方法),我仍然将HTML作为响应。如何获取JSON作为响应?我还需要说明什么? 最佳答案 您应该将.json附加到请求的url,提供的格式在routes.rb的路径中定义。这
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R
我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm