微信内网页开发 - 公众号支付

接口文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_4

开发步骤:

一、设置支付授权目录

微信内网页开发 - 公众号支付

二、流程

1、前端H5页面请求服务端生成唯一订单号(包括用户信息,支付金额,商品信息等),服务端在数据库创建一条新记录

2、前端H5页面请求服务端Perl CGI脚本进行支付: 例如https:/xxxx/cgi-bin/pay.pl?do=jspay&order_id=xxxxxxx&openid=xxxxxx

////脚本处理

if ($cgi->param('do') eq "jspay") {

}

3、CGI脚本调用接口访问数据库,获取支付记录信息,调用微信统一下单接口

https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1

其中notify_url直接填本CGI脚本文件的地址,通知url必须为直接可访问的url,不能携带参数。示例:notify_url:"https:/xxxx/cgi-bin/pay.pl"

use CGI;

use warnings;

use JSON;

use utf8;

use Digest::MD5 qw/md5_hex/;

use HTTP::Request;

use HTTP::Headers;

use LWP::UserAgent;

use Encode;

use XML::Simple;

use Data::Dumper;

if ($cgi->param('do') eq "jspay") {

my $order_id  = $cgi->param('order_id');

my $openid  = $cgi->param('openid');

my $order_info = get_order_by_id($order_id);

my $wx_order_info;

$wx_order_info->{attach}="$order_id"; #order_id用于后面更新服务器数据

$wx_order_info->{body}="Happy New Year";

$wx_order_info->{mch_id}=$MCH_ID;

$wx_order_info->{nonce_str}=nonce_str();

$wx_order_info->{openid}=$openid;

$wx_order_info->{trade_type}="JSAPI";

$wx_order_info->{out_trade_no}=$order_info->{_id};

$wx_order_info->{total_fee}=100*$order_info->{rmb};

$wx_order_info->{notify_url}="http://xxxxx/cgi-bin/pay.pl";

$wx_order_info->{spbill_create_ip}="127.0.0.1";

$wx_order_info->{sign_type}="MD5";

$wx_order_info->{sign} = sign($wx_order_info); #签名

my $request_xml = create_xml_data($wx_order_info); #转成XML格式

#发送POST请求 统一下单接口

my $header = HTTP::Headers->new( Content_Type => 'text/xml; charset=utf8', );

my $http_request = HTTP::Request->new( POST => "https://api.mch.weixin.qq.com/pay/unifiedorder", $header, $request_xml );

my $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0, SSL_verify_mode => 0x00 });

#解析响应

my $response = $ua->request($http_request);

my $response_json;

if ($response->message ne "OK" && $response->is_success ne "1") { #出错,或者timeout了

$response_json->{status} = "99999";

$response_json->{message} = $response->message;

$response_json->{is_success} = $response->is_success;

} else {

$response_json = parse_xml_response( $response->content());

}

}

sub create_xml_data {

my $params = $_[0];

my $xml  = '<xml>';

foreach ( keys %$params ) {

if ( $params->{$_} and $params->{$_} !~ /^\d+$/ ) {

$xml .= sprintf( '<%s><![CDATA[%s]]></%s>', $_, $params->{$_}, $_ );

} else {

$xml .= sprintf( '<%s>%s</%s>', $_, $params->{$_}, $_ );

}

}

$xml .= '</xml>';

return $xml;

}

sub sign {

my $params  = $_[0];

my $app_key = $APP_KEY;

my $params_sign = {};

foreach ( keys %$params ) {

next if $_ eq 'sign';

next unless defined $params->{$_};

Encode::_utf8_off( $params->{$_} );

$params_sign->{$_} = $params->{$_};

}

my $sign_string = join( '&',

map { sprintf( '%s=%s', $_, $params_sign->{$_} ) }

sort { $a cmp $b } keys %$params_sign );

$sign_string .= sprintf( '&key=%s', $app_key );

return uc md5_hex $sign_string;

}

sub parse_xml_response {

my $content = $_[0];

my $result  = XMLin($content);

return $result;

}

4、CGI脚本根据统一下单接口返回的参数,调用jsapi进行支付

5、支付成功或者失败分别重定向到响应的H5页面

{

my $pay_info = $json->decode($response_json->{pay_info});

my $js_obj;

$js_obj->{appId} = $pay_info->{appId}; 

$js_obj->{timeStamp} = $pay_info->{timeStamp};

$js_obj->{nonceStr} = $pay_info->{nonceStr};

$js_obj->{package} = $pay_info->{package}; #提交后微信会返回xml,其中包含了prepay_id:

$js_obj->{signType} = $pay_info->{signType};

$js_obj->{paySign} = $pay_info->{paySign};

$js_obj->{order_info} = $order_info;

write_log("json:".Dumper($js_obj)." $pay_info");

print_html($js_obj);

}

sub print_html {

my $js_obj=$_[0];

my $succ_url = "http://xxxx/pay_success.html"; #支付成功跳转页面

my $fail_url = "http://xxxx/pay_failed.html"; #支付失败跳转页面

print "Content-type:text/html\n\n";

print<<EOF;

<!DOCTYPE html>

    <html>

      <header>

        <title>微信支付</title>

        <meta http-equiv=Content-Type content=text/html; charset=utf8>

      </header> 

      <body>

        <script type="text/javascript">       

          function onBridgeReady(){

            WeixinJSBridge.invoke(

              'getBrandWCPayRequest', {

              "appId" : "$js_obj->{appId}",      //公众号名称,由商户传入

              "timeStamp":"$js_obj->{timeStamp}",//时间戳,自1970年以来的秒数

              "nonceStr" :"$js_obj->{nonceStr}",//随机串

              "package" : "$js_obj->{package}",

              "signType" : "MD5", 

              "paySign" : "$js_obj->{paySign}"   //微信签名

            },

            function(res) {

              if (res.err_msg == "get_brand_wcpay_request:ok") {

              location.href="$succ_url";

              }

              else if(res.err_msg == "get_brand_wcpay_request:cancel"){

              location.href="$fail_url";

              }else{

              location.href="$fail_url";

              }

            }

            );

          };

          if (typeof WeixinJSBridge == "undefined"){

            if( document.addEventListener ){

              document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);

            }else if (document.attachEvent){

              document.attachEvent('WeixinJSBridgeReady', onBridgeReady);

              document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);

            }

          }else{

            onBridgeReady();

          }

        </script>  

      </body>

    </html>

EOF

exit;

}

6、CGI脚本收到微信的异步支付结果通知,调用服务端接口访问数据库,更新订单支付结果,以及用户的充值金额。

https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7

} elsif ($cgi->param("POSTDATA")) {#微信notify_url返回的

my $post_data=$cgi->param("POSTDATA");

my $xml_return = $post_data;

my $json_return =  parse_xml_response($xml_return);  #本次支付是否成功的xml文档

write_log("xml:".$xml_return."\njson:".Dumper($json_return));

my $pay_result = "success";

if ($json_return->{status} ne "0" || $json_return->{result_code} ne "0") {

$pay_result = "faild"; #支付失败(无订单号信息),直接退出

} elsif ($json_return->{pay_result} ne "0") {

$pay_result = "failed";#支付失败(有订单号信息)

my $ret = update_order_by_id($json_return); #调用服务端接口更新订单支付结果,以及用户的充值金额。

}

print_html_notify_rsp("success");

}

sub print_html_notify_rsp{

my $status=$_[0];

print "Content-type:text/html\n\n";

print $status;

exit;

}

相关推荐