import { Inject , Injectable } from '@nestjs/common' ; import { createDecipheriv , createHash } from 'crypto' ; import { IncomingMessage } from 'http' ; import { WeChatPayConfig } from '../../../common' ; import { XmlUtil } from '../../../shared/utils/xml.util' ; import { WeChatPayConfigProvider } from '../constants/wechat.constant' ; import { WeChatPayNotifyRes , WeChatRefundNotifyRes } from '../interfaces/notify.interface' ; import { WeChatSignUtil } from './sign.util' ; /**
 * 微信支付通知解析工具
 */ @ Injectable ( ) export class WeChatNotifyParserUtil { constructor ( @ Inject ( WeChatPayConfigProvider ) protected readonly config , @ Inject ( XmlUtil ) private readonly xmlUtil , @ Inject ( WeChatSignUtil ) private readonly signUtil ) { } param req public async parsePayNotify ( req ) { const data = await this . receiveReqData ( req , 'pay' ) ; const signType = this . config . sign_type ; const result = await this . xmlUtil . parseObjFromXml < WeChatPayNotifyRes > ( data ) ; if ( result . return_code === 'FAIL' ) { return result ; } if ( result . sign && result . sign !== this . signUtil . sign ( result , signType ) ) { return undefined ; } return result ; } param req public async parseRefundNotify ( req ) { const data = await this . receiveReqData ( req , 'refund' ) ; const result = await this . xmlUtil . parseObjFromXml < WeChatRefundNotifyRes > ( data ) ; if ( result . return_code === 'FAIL' ) { return result ; } if ( ! ( result as any ) . req_info ) { return undefined ; } try { const secretKey = this . config . secretKey ; const cryptedBase64Str = Buffer . from ( ( result as any ) . req_info ) . toString ( 'base64' ) ; const secretKeyMD5 = createHash ( 'md5' ) . update ( secretKey ) . digest ( 'hex' ) . toLocaleLowerCase ( ) ; const decipher = createDecipheriv ( 'aes-256-ecb' , secretKeyMD5 , '' ) ; const decryptedStr = Buffer . concat ( [ decipher . update ( cryptedBase64Str , 'base64' ) , decipher . final ( ) ] ) . toString ( ) ; Object . assign ( result , JSON . parse ( decryptedStr ) ) ; } catch ( error ) { return undefined ; } return result ; } /**
     * 生成通知成功返回值
     */ public generateSuccessMessage ( ) { return this . xmlUtil . convertObjToXml ( { return_code : 'SUCCESS' } ) ; } param errMsg public generateFailMessage ( errMsg ) { return this . xmlUtil . convertObjToXml ( { return_code : 'FAIL' , return_msg : errMsg } ) ; } param req private async receiveReqData ( req , type ) { const errType = type === 'pay' ? '支付' : '退款' ; let xmlStr ; try { xmlStr = await new Promise < string > ( ( resolve , reject ) => { let data = '' ; req . on ( 'data' , chunk => { data += chunk ; } ) ; req . on ( 'end' , ( ) => { resolve ( data ) ; } ) ; req . on ( 'error' , error => { reject ( error ) ; } ) ; } ) ; } catch ( error ) { throw new Error ( `解析微信${ errType }结果通知发生网络异常: ${ error . toString ( ) }` ) ; } if ( xmlStr . trim ( ) . length === 0 ) { throw new Error ( `解析微信${ errType }结果通知异常: 数据为空` ) ; } return xmlStr ; } }