<template>
  <view class="app">
    <view v-if="errMsg != ''">
      <text class="err-msg">注意：{{ errMsg }}</text>
    </view>
    <view>
      <view class="label">支付单号：</view>
      <view><input class="input" v-model="out_trade_no" placeholder="点击发起支付会自动生成" /></view>
    </view>
    <view>
      <view class="label">支付金额（单位分，100=1元）：</view>
      <view><input class="input" v-model="total_fee" /></view>
    </view>
    <!-- #ifdef APP || H5 -->
    <button class="button" @click="open()">打开收银台（弹窗模式）</button>
    <view v-if="!isPcCom">
      <button class="button" @click="toPayDesk">打开收银台（新页面模式）</button>
    </view>
    <!-- #endif -->

    <!-- #ifdef MP-WEIXIN || H5 || APP -->
    <button class="button" @click="createOrder('wxpay')">发起支付（微信）</button>
    <!-- #endif -->

    <!-- #ifdef MP-ALIPAY || H5 || APP -->
    <button class="button" @click="createOrder('alipay')">发起支付（支付宝）</button>
    <!-- #endif -->

    <!-- #ifdef APP -->
    <button class="button" @click="createQRcode('alipay')">APP扫码支付（支付宝）</button>
    <!-- #endif -->

    <button class="button" @click="getOrderPopup(true)">查询支付状态</button>
    <button class="button"
      @click="pageTo('/uni_modules/uni-pay-x/pages/success/success?out_trade_no=test2024030501-1&order_no=test2024030501&total_fee=1&adpid=1000000001&return_url=/pages/API/request-payment/request-payment/order-detail')">支付成功页面示例</button>
    <!-- #ifdef APP-IOS -->
    <button class="button"
      @click="pageTo('/pages/API/virtual-payment/virtual-payment-uni-pay')">苹果虚拟支付示例（iOS内购）</button>
    <!-- #endif -->
    <!-- 查询支付的弹窗 -->
    <uni-pay-popup ref="getOrderPopupRef" type="center">
      <scroll-view direction="vertical" class="get-order-popup">
        <view class="label">插件支付单号：</view>
        <view class="mt20">
          <input class="input pd2030" v-model="out_trade_no" placeholder="请输入" />
          <view><text class="tips">插件支付单号和第三方交易单号2选1填即可</text> </view>
        </view>
        <view class="label">第三方交易单号：</view>
        <view class="mt20">
          <input class="input pd2030" v-model="transaction_id" placeholder="请输入" />
          <view class="tips"><text class="tips">可从支付宝账单（订单号）、微信账单（交易单号）中复制</text></view>
        </view>
        <view class="mt20">
          <button class="button" @click="getOrder">查询支付状态</button>
        </view>
        <view class="mt20" v-if="getOrderRes['transaction_id']">
          <view class="table">
            <view class="table-tr">
              <view class="table-td label"><text class="text align-left">订单描述</text></view>
              <view class="table-td"><text class="text align-right">{{ getOrderRes['description'] }}</text></view>
            </view>
            <view class="table-tr">
              <view class="table-td label"><text class="text align-left">支付金额</text></view>
              <view class="table-td"><text
                  class="text align-right">{{ amountFormat(getOrderRes.getNumber('total_fee')) }}</text></view>
            </view>
            <view class="table-tr">
              <view class="table-td label"><text class="text align-left">支付方式</text></view>
              <view class="table-td"><text
                  class="text align-right">{{ providerFormat(getOrderRes['provider'] as string) }}</text></view>
            </view>
            <view class="table-tr">
              <view class="table-td label"><text class="text align-left">第三方交易单号</text></view>
              <view class="table-td"><text class="text align-right">{{ getOrderRes['transaction_id'] }}</text></view>
            </view>
            <view class="table-tr">
              <view class="table-td label"><text class="text align-left">插件支付单号</text></view>
              <view class="table-td"><text class="text align-right">{{ getOrderRes['out_trade_no'] }}</text></view>
            </view>
            <view class="table-tr">
              <view class="table-td label"><text class="text align-left">回调状态</text></view>
              <view class="table-td"><text
                  class="text align-right">{{ getOrderRes.getBoolean('user_order_success') != null && getOrderRes.getBoolean('user_order_success') == true ? "成功" : "异常" }}</text>
              </view>
            </view>
          </view>
        </view>
      </scroll-view>
    </uni-pay-popup>

    <!-- #ifdef APP -->
    <!-- <button class="button" v-if="isIosAppCom" @click="pageTo('/pages/iosiap/iosiap')">苹果内购示例</button> -->
    <!-- #endif -->

    <!-- <button class="button" @click="refund">发起退款</button>
		<view><text class="tips">发起退款需要admin权限，本示例未对接登录功能</text></view>
		<button class="button" @click="getRefund">查询退款状态</button>
		<button class="button" @click="closeOrder">关闭订单</button> -->

    <!-- #ifdef H5 -->
    <button class="button" v-if="h5Env === 'h5-weixin'" @click="getWeiXinJsCode('snsapi_base')">公众号获取openid示例</button>
    <!-- #endif -->
    <!-- 统一支付组件，注意：vue3下ref不可以等于组件名，因此这里ref="pay" 而不能是 ref="uniPay" -->
    <uni-pay ref="payRef" :adpid="adpid" height="900rpx"
      return-url="/pages/API/request-payment/request-payment/order-detail" logo="/static/logo.png" @success="onSuccess"
      @create="onCreate" @fail="onFail" @cancel="onCancel"></uni-pay>
  </view>
</template>

<script>
  export default {
    data() {
      return {
        total_fee: "1", // 支付金额，单位分 100 = 1元
        order_no: "", // 业务系统订单号（即你自己业务系统的订单表的订单号）
        out_trade_no: "", // 插件支付单号
        description: "测试订单", // 支付描述
        type: "test", // 支付回调类型 如 recharge 代表余额充值 goods 代表商品订单（可自定义，任意英文单词都可以，只要你在 uni-pay-co/notify/目录下创建对应的 xxx.js文件进行编写对应的回调逻辑即可）
        openid: "", // 微信公众号需要
        custom: {
          a: "a",
          b: 1
        } as UTSJSONObject,
        adpid: "1000000001", // uni-ad的广告位id

        transaction_id: "", // 查询订单接口的查询条件
        getOrderRes: {} as UTSJSONObject, // 查询订单支付成功后的返回值
        useUniCloud: true,
        errMsg: ""
      }
    },
    onLoad(options) {
      console.log('onLoad: ', options);
      this.useUniCloud = this.checkUniCloud();
      // #ifdef H5
      // 微信公众号特殊逻辑开始-----------------------------------------------------------
      // 以下代码仅为获取openid，正常你自己项目应该是登录后才能支付，登录后已经拿到openid，无需编写下面的代码
      if (this.h5Env == 'h5-weixin') {
        let openid = uni.getStorageSync("uni-pay-weixin-h5-openid");
        let oldCode = uni.getStorageSync("uni-pay-weixin-h5-code");
        if (openid != null && openid != "") {
          this.openid = openid;
        }
        let code = options['code'] as string;
        let state = options['state'] as string;
        // 如果code和state有值，且此code没有被使用过，则执行获取微信公众号的openid
        if (code != null && code != "" && state != null && state != "" && code != oldCode) {
          // 获取微信公众号的openid
          setTimeout(() => {
            this.getOpenid({
              provider: "wxpay",
              code
            });
          }, 300);
        } else if (!openid) {
          // 如果openid为空，则执行微信公众号的网页授权登录逻辑
          setTimeout(() => {
            this.getWeiXinJsCode('snsapi_base');
          }, 300);
        }
      }
      // 微信公众号特殊逻辑结束-----------------------------------------------------------
      // #endif
    },
    methods: {
      /**
       * 发起支付（唤起收银台，如果只有一种支付方式，则收银台不会弹出来，会直接使用此支付方式）
       * 在调用此api前，你应该先创建自己的业务系统订单，并获得订单号 order_no，把order_no当参数传给此api，而示例中为了简化跟支付插件无关的代码，这里直接已时间戳生成了order_no
       */
      open() {
        this.order_no = `test` + Date.now();
        this.out_trade_no = `${this.order_no}-1`;
        // 打开支付收银台
        const payInstance = this.$refs["payRef"] as UniPayComponentPublicInstance;
        payInstance.open({
          total_fee: parseInt(this.total_fee), // 支付金额，单位分 100 = 1元
          order_no: this.order_no, // 业务系统订单号（即你自己业务系统的订单表的订单号）
          out_trade_no: this.out_trade_no, // 插件支付单号
          description: this.description, // 支付描述
          type: this.type, // 支付回调类型
          openid: this.openid, // 微信公众号需要
          custom: this.custom, // 自定义数据
        });
      },
      /**
       * 发起支付（不唤起收银台，手动指定支付方式）
       * 在调用此api前，你应该先创建自己的业务系统订单，并获得订单号 order_no，把order_no当参数传给此api，而示例中为了简化跟支付插件无关的代码，这里直接已时间戳生成了order_no
       */
      createOrder(provider : string) {
        this.order_no = `test` + Date.now();
        this.out_trade_no = `${this.order_no}-1`;
        // 发起支付
        const payInstance = this.$refs["payRef"] as UniPayComponentPublicInstance;
        payInstance.createOrder({
          provider: provider, // 支付供应商
          total_fee: parseInt(this.total_fee), // 支付金额，单位分 100 = 1元
          order_no: this.order_no, // 业务系统订单号（即你自己业务系统的订单表的订单号）
          out_trade_no: this.out_trade_no, // 插件支付单号
          description: this.description, // 支付描述
          type: this.type, // 支付回调类型
          openid: this.openid, // 微信公众号需要
          custom: this.custom, // 自定义数据
        });
      },
      /**
       * 生成支付独立二维码（只返回支付二维码）
       * 在调用此api前，你应该先创建自己的业务系统订单，并获得订单号 order_no，把order_no当参数传给此api，而示例中为了简化跟支付插件无关的代码，这里直接已时间戳生成了order_no
       */
      createQRcode(provider : string) {
        this.order_no = `test` + Date.now();
        this.out_trade_no = `${this.order_no}-1`;
        // 发起支付
        const payInstance = this.$refs["payRef"] as UniPayComponentPublicInstance;
        payInstance.createOrder({
          provider: provider, // 支付供应商
          total_fee: parseInt(this.total_fee), // 支付金额，单位分 100 = 1元
          order_no: this.order_no, // 业务系统订单号（即你自己业务系统的订单表的订单号）
          out_trade_no: this.out_trade_no, // 插件支付单号
          description: this.description, // 支付描述
          type: this.type, // 支付回调类型
          qr_code: true, // 强制扫码支付
          openid: this.openid, // 微信公众号需要
          custom: this.custom, // 自定义数据
        });
      },
      /**
       * 前往自定义收银台页面
       * 在调用此api前，你应该先创建自己的业务系统订单，并获得订单号 order_no，把order_no当参数传给此api，而示例中为了简化跟支付插件无关的代码，这里直接已时间戳生成了order_no
       */
      toPayDesk() {
        this.order_no = `test` + Date.now();
        this.out_trade_no = `${this.order_no}-1`;
        let options = {
          total_fee: parseInt(this.total_fee), // 支付金额，单位分 100 = 1元
          order_no: this.order_no, // 业务系统订单号（即你自己业务系统的订单表的订单号）
          out_trade_no: this.out_trade_no, // 插件支付单号
          description: this.description, // 支付描述
          type: this.type, // 支付回调类型
          openid: this.openid, // 微信公众号需要
          custom: this.custom, // 自定义数据
        };
        let optionsStr = encodeURI(JSON.stringify(options));
        uni.navigateTo({
          url: `/uni_modules/uni-pay-x/pages/pay-desk/pay-desk?options=${optionsStr}`
        });
      },
      // 打开查询订单的弹窗
      getOrderPopup(key : boolean) {
        const getOrderPopupInstance = this.$refs["getOrderPopupRef"] as UniPayPopupComponentPublicInstance;
        if (key) {
          getOrderPopupInstance.open();
        } else {
          getOrderPopupInstance.close();
        }
      },
      // 查询支付状态
      async getOrder() : Promise<void> {
        this.getOrderRes = {} as UTSJSONObject;
        const payInstance = this.$refs["payRef"] as UniPayComponentPublicInstance;
        let getOrderData = {
          await_notify: true
        } as UTSJSONObject;
        if (this.transaction_id != "") {
          getOrderData['transaction_id'] = this.transaction_id;
        } else if (this.out_trade_no != "") {
          getOrderData['out_trade_no'] = this.out_trade_no;
        }
        let res = await payInstance.getOrder(getOrderData);
        if (res['errCode'] == 0) {
          this.getOrderRes = res.getJSON('pay_order') as UTSJSONObject;
          let obj = {
            "-1": "已关闭",
            "1": "已支付",
            "0": "未支付",
            "2": "已部分退款",
            "3": "已全额退款"
          } as UTSJSONObject;
          let status = res['status'] as number;
          let statusStr = status + "";
          let title = obj[statusStr] as string;
          uni.showToast({
            title: title,
            icon: "none"
          });
        }
      },
      // 发起退款
      async refund() : Promise<void> {
        const payInstance = this.$refs["payRef"] as UniPayComponentPublicInstance;
        let res = await payInstance.refund({
          out_trade_no: this.out_trade_no, // 插件支付单号
        });
        if (res['errCode'] == 0) {
          uni.showToast({
            title: res['errMsg'] as string,
            icon: "none"
          });
        }
      },
      // 查询退款状态
      async getRefund() : Promise<void> {
        const payInstance = this.$refs["payRef"] as UniPayComponentPublicInstance;
        let res = await payInstance.getRefund({
          out_trade_no: this.out_trade_no, // 插件支付单号
        });
        if (res['errCode'] == 0) {
          uni.showModal({
            content: res['errMsg'] as string,
            showCancel: false
          });
        }
      },
      // 关闭订单
      async closeOrder() : Promise<void> {
        const payInstance = this.$refs["payRef"] as UniPayComponentPublicInstance;
        let res = await payInstance.closeOrder({
          out_trade_no: this.out_trade_no, // 插件支付单号
        });
        if (res['errCode'] == 0) {
          uni.showModal({
            content: res['errMsg'] as string,
            showCancel: false
          });
        }
      },
      // #ifdef H5
      // 获取公众号code
      async getWeiXinJsCode(scope = "snsapi_base") : Promise<void> {
        const payInstance = this.$refs["payRef"] as UniPayComponentPublicInstance;
        let res = await payInstance.getProviderAppId({
          provider: "wxpay",
          provider_pay_type: "jsapi"
        });
        if (res['appid'] != null && res['appid'] != "") {
          let appid = res['appid'] as string;
          let redirect_uri = window.location.href.split("?")[0];
          let url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${redirect_uri}&response_type=code&scope=${scope}&state=STATE#wechat_redirect`;
          window.location.href = url;
        }
      },
      // 获取公众号openid
      async getOpenid(data : UTSJSONObject) : Promise<void> {
        const payInstance = this.$refs["payRef"] as UniPayComponentPublicInstance;
        let res = await payInstance.getOpenid(data);
        if (res['openid'] != null && res['openid'] != "") {
          let openid = res['openid'] as string;
          let code = data['code'] as string;
          this.openid = openid;
          console.log('openid: ', openid)
          // 将openid缓存到本地
          uni.setStorageSync("uni-pay-weixin-h5-openid", openid);
          uni.setStorageSync("uni-pay-weixin-h5-code", code);
          uni.showToast({
            title: "已获取到openid，可以开始支付",
            icon: "none"
          });
        }
      },
      // #endif
      // 监听事件 - 支付订单创建成功（此时用户还未支付）
      onCreate(res : UTSJSONObject) {
        console.log('create: ', res);
        // 如果只是想生成支付二维码，不需要组件自带的弹窗，则在这里可以获取到支付二维码 qr_code_image
      },
      // 监听事件 - 支付成功
      onSuccess(res : UTSJSONObject) {
        console.log('success: ', res);
        let user_order_success = res.getBoolean('user_order_success');
        if (user_order_success != null && user_order_success != true) {
          // 代表用户已付款，且你自己写的回调成功并正确执行了

        } else {
          // 代表用户已付款，但你自己写的回调执行失败（通常是因为你的回调代码有问题）

        }
      },
      // 监听事件 - 支付失败
      onFail(err : UTSJSONObject) {
        console.log('fail: ', err)

      },
      // 监听事件 - 取消支付
      onCancel(err : UTSJSONObject) {
        console.log('cancel: ', err)
        uni.showToast({
          title: "用户取消了支付",
          icon: "none"
        });
      },
      // 页面跳转
      pageTo(url : string) {
        uni.navigateTo({
          url
        });
      },
      // provider格式化
      providerFormat(provider ?: string) : string {
        if (provider == null) {
          return "";
        }
        let providerObj = {
          "wxpay": "微信支付",
          "alipay": "支付宝支付",
          "appleiap": "ios内购"
        } as UTSJSONObject;
        let providerStr = providerObj[provider] as string;
        return providerStr;
      },
      // amount格式化
      amountFormat(totalFee : number | null) : string {
        if (totalFee == null) {
          return "0";
        } else {
          return (totalFee / 100).toFixed(2)
        }
      },
      checkUniCloud() : boolean {
        let useUniCloud = false;
        if (typeof uniCloud != 'undefined' && typeof uniCloud.config == "object" && uniCloud.config.spaceId != "") {
          useUniCloud = true;
        }
        if (!useUniCloud) {
          let errMsg = "本示例依赖 uniCloud，请先关联服务空间，填写正确的支付配置，支付配置文件地址：/uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/uni-pay/config.js";
          this.errMsg = errMsg;
          uni.showModal({
            title: "提示",
            content: errMsg,
            showCancel: false
          })
          console.error(errMsg);
        }
        return useUniCloud;
      }
    },
    computed: {
      // 计算当前H5环境
      h5Env() : string {
        // #ifdef H5
        const ua = window.navigator.userAgent.toLowerCase();
        const isWeixin = /micromessenger/i.test(ua);
        const isAlipay = /alipay/i.test(ua);
        const isMiniProgram = /miniprogram/i.test(ua);
        if (isWeixin) {
          if (isMiniProgram) {
            return "mp-weixin";
          } else {
            return "h5-weixin";
          }
        } else if (isAlipay) {
          if (isMiniProgram) {
            return "mp-alipay";
          } else {
            return "h5-alipay";
          }
        }
        return "h5";
        // #endif
        return "";
      },
      // 计算当前是否是ios app
      isIosAppCom() : boolean {
        let info = uni.getSystemInfoSync();
        return info.uniPlatform == 'app' && info.osName == 'ios' ? true : false;
      },
      // 计算当前是否是PC环境
      isPcCom() : boolean {
        let isPC = false;
        // #ifdef H5
        let info = uni.getSystemInfoSync();
        isPC = info.deviceType == 'pc' ? true : false;
        // #endif
        return isPC;
      }
    },
  }
</script>

<style lang="scss" scoped>
  .app {
    padding: 30rpx;
  }

  .input {
    border: 1px solid #f3f3f3;
    padding: 10rpx;
    width: 100%;
    box-sizing: border-box;
    height: 80rpx;
    background: #FFF;
  }

  .button {
    margin-top: 20rpx;
  }

  .label {
    margin: 10rpx 0;
  }

  .tips {
    margin: 20rpx 0;
    font-size: 24rpx;
    color: #565656;
  }

  .get-order-popup {
    background-color: #ffffff;
    padding: 30rpx;
    height: 900rpx;
    border-radius: 20rpx;
    width: 690rpx;
  }

  .mt20 {
    margin-top: 20rpx;
  }

  .pd2030 {
    padding: 20rpx 30rpx;
  }

  .table {
    .table-tr {
      display: flex;
      flex-direction: row;
      justify-content: center;
      align-items: center;
      padding: 10rpx 0;
    }

    .table-td {
      flex: 1;
    }

    .align-left {
      text-align: left;
    }

    .align-right {
      text-align: right;
    }

    .label {
      width: 180rpx;
    }

    .text {
      font-size: 24rpx;
    }
  }

  .err-msg {
    color: red;
    font-size: 14px;
  }
</style>
