Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 895988cf27 | |||
| 87c597c3fe | |||
| f536b12f00 | |||
| d3b0f436fa | |||
| f5b6bd54f4 | |||
| 59a3b45c77 | |||
| dd96d653d1 | |||
| 97d1c32310 | |||
| d7733525bc | |||
| 62e57b99a0 | |||
| 24655a4644 | |||
| 4ca64c8205 | |||
| d9dbfb3ca6 | |||
| 1ff0203511 | |||
| 561b105d31 | |||
| 3d423acec2 | |||
| d76680a7a2 | |||
| aeb4f3d7fb | |||
| a3aa8e8ac3 | |||
| de4f58d3e3 | |||
| 76bf8cc2fc | |||
| d9713d00e7 |
+4
-3
@@ -19,9 +19,10 @@
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"php": ">= 8.0",
|
||||
"game-worker/kiri-client": "~v2.8",
|
||||
"psr/container": "*"
|
||||
"php": ">= 8.4",
|
||||
"game-worker/kiri-client": "^v2.5",
|
||||
"psr/container": "*",
|
||||
"ext-openssl": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"swoole/ide-helper": "@dev"
|
||||
|
||||
+32
-2
@@ -6,6 +6,29 @@ namespace wchat\wx;
|
||||
|
||||
use wchat\common\Result;
|
||||
|
||||
//if (PHP_VERSION > '8.2') {
|
||||
// enum Scene
|
||||
// {
|
||||
//
|
||||
// case SCENE_INFO;
|
||||
// case SCENE_REPLY;
|
||||
// case SCENE_DISCAZ;
|
||||
// case SCENE_SHEJIAO;
|
||||
//
|
||||
//
|
||||
// public function getValue(): int
|
||||
// {
|
||||
// return match ($this) {
|
||||
// self::SCENE_INFO => 1,
|
||||
// self::SCENE_REPLY => 2,
|
||||
// self::SCENE_DISCAZ => 3,
|
||||
// self::SCENE_SHEJIAO => 4,
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
/**
|
||||
* Class SecCheck
|
||||
* @package wchat
|
||||
@@ -65,16 +88,23 @@ class SecCheck extends SmallProgram
|
||||
|
||||
/**
|
||||
* @param string $content
|
||||
* @param int $scene
|
||||
* @param string $openId
|
||||
* @return Result
|
||||
*/
|
||||
public function text(string $content): Result
|
||||
public function text(string $content, int $scene, string $openId): Result
|
||||
{
|
||||
if (empty($content)) {
|
||||
return $this->sendError('文件不存在', 404);
|
||||
}
|
||||
$requestUrl = $this->_msgUrl . $this->payConfig->getAccessToken();
|
||||
|
||||
return $this->post('api.weixin.qq.com', $requestUrl, ['content' => $content]);
|
||||
return $this->post('api.weixin.qq.com', $requestUrl, json_encode([
|
||||
'content' => $content,
|
||||
'version' => 2,
|
||||
'scene' => $scene,
|
||||
'openid' => $openId
|
||||
], JSON_UNESCAPED_UNICODE), 'application/json');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
<?php
|
||||
|
||||
namespace wchat\wx\V3\Libs;
|
||||
|
||||
use Exception;
|
||||
|
||||
class TransferDetail
|
||||
{
|
||||
|
||||
|
||||
private string $out_bill_no;
|
||||
private string $transfer_scene_id;
|
||||
private string $openid;
|
||||
private string $user_name = '';
|
||||
private int $transfer_amount;
|
||||
private string $transfer_remark;
|
||||
private string $notify_url = '';
|
||||
private string $user_recv_perception = '';
|
||||
private array $transfer_scene_report_infos;
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getOutBillNo(): string
|
||||
{
|
||||
return $this->out_bill_no;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $out_bill_no
|
||||
* @return void
|
||||
*/
|
||||
public function setOutBillNo(string $out_bill_no): void
|
||||
{
|
||||
$this->out_bill_no = $out_bill_no;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTransferSceneId(): string
|
||||
{
|
||||
return $this->transfer_scene_id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $transfer_scene_id
|
||||
* @return void
|
||||
*/
|
||||
public function setTransferSceneId(string $transfer_scene_id): void
|
||||
{
|
||||
$this->transfer_scene_id = $transfer_scene_id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getOpenid(): string
|
||||
{
|
||||
return $this->openid;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $openid
|
||||
* @return void
|
||||
*/
|
||||
public function setOpenid(string $openid): void
|
||||
{
|
||||
$this->openid = $openid;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUserName(): string
|
||||
{
|
||||
return $this->user_name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $user_name
|
||||
* @return void
|
||||
*/
|
||||
public function setUserName(string $user_name): void
|
||||
{
|
||||
$this->user_name = $user_name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getTransferAmount(): int
|
||||
{
|
||||
return $this->transfer_amount;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param int $transfer_amount
|
||||
* @return void
|
||||
*/
|
||||
public function setTransferAmount(int $transfer_amount): void
|
||||
{
|
||||
$this->transfer_amount = $transfer_amount;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTransferRemark(): string
|
||||
{
|
||||
return $this->transfer_remark;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $transfer_remark
|
||||
* @return void
|
||||
*/
|
||||
public function setTransferRemark(string $transfer_remark): void
|
||||
{
|
||||
$this->transfer_remark = $transfer_remark;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getNotifyUrl(): string
|
||||
{
|
||||
return $this->notify_url;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $notify_url
|
||||
* @return void
|
||||
*/
|
||||
public function setNotifyUrl(string $notify_url): void
|
||||
{
|
||||
$this->notify_url = $notify_url;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getUserRecvPerception(): string
|
||||
{
|
||||
return $this->user_recv_perception;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $user_recv_perception
|
||||
* @return void
|
||||
*/
|
||||
public function setUserRecvPerception(string $user_recv_perception): void
|
||||
{
|
||||
$this->user_recv_perception = $user_recv_perception;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array<TransferSceneReportInfo>
|
||||
*/
|
||||
public function getTransferSceneReportInfos(): array
|
||||
{
|
||||
return $this->transfer_scene_report_infos;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param array $transfer_scene_report_infos
|
||||
* @return void
|
||||
*/
|
||||
public function setTransferSceneReportInfos(TransferSceneReportInfo ...$transfer_scene_report_infos): void
|
||||
{
|
||||
$this->transfer_scene_report_infos = $transfer_scene_report_infos;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
$transfer_scene_report_infos = [];
|
||||
foreach ($this->transfer_scene_report_infos as $transfer_scene_report_info) {
|
||||
$transfer_scene_report_infos[] = $transfer_scene_report_info->toArray();
|
||||
}
|
||||
$array = [
|
||||
'out_bill_no' => $this->out_bill_no,
|
||||
'transfer_scene_id' => $this->transfer_scene_id,
|
||||
'openid' => $this->openid,
|
||||
'transfer_amount' => $this->transfer_amount,
|
||||
'transfer_remark' => $this->transfer_remark,
|
||||
'transfer_scene_report_infos' => $transfer_scene_report_infos,
|
||||
];
|
||||
foreach ($array as $key => $value) {
|
||||
if (empty($value)) {
|
||||
throw new Exception('必填项' . $key . '不能为空.');
|
||||
}
|
||||
}
|
||||
if (!empty($this->user_name)) {
|
||||
$array['user_name'] = $this->user_name;
|
||||
}
|
||||
if (!empty($this->notify_url)) {
|
||||
$array['notify_url'] = $this->notify_url;
|
||||
}
|
||||
if (!empty($this->user_recv_perception)) {
|
||||
$array['user_recv_perception'] = $this->user_recv_perception;
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace wchat\wx\V3\Libs;
|
||||
|
||||
use Arrayable;
|
||||
|
||||
readonly class TransferSceneReportInfo implements Arrayable
|
||||
{
|
||||
|
||||
|
||||
public function __construct(
|
||||
public string $info_type,
|
||||
public string $info_content,
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
"info_type" => $this->info_type,
|
||||
"info_content" => $this->info_content
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace wchat\wx\V3;
|
||||
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
use wchat\wx\SmallProgram;
|
||||
|
||||
class TransferBatches extends SmallProgram
|
||||
{
|
||||
use WxV3PaymentTait;
|
||||
|
||||
|
||||
/**
|
||||
* @param TransferDetail $detail
|
||||
* @return array
|
||||
* @throws
|
||||
*/
|
||||
#[ArrayShape([])]
|
||||
public function transfer(TransferDetail $detail): array
|
||||
{
|
||||
$payConfig = $this->getPayConfig();
|
||||
$body = [];
|
||||
if ($payConfig->typeIsApp()) {
|
||||
$body['appid'] = $payConfig->pay->wx->appId;
|
||||
} else {
|
||||
$body['appid'] = $payConfig->appId;
|
||||
}
|
||||
$body['out_batch_no'] = $detail->out_detail_no;
|
||||
$body["batch_name"] = $payConfig->getBody();
|
||||
$body["body"] = $payConfig->getBody();
|
||||
$body["batch_remark"] = $payConfig->getBody();
|
||||
$body["total_amount"] = $detail->transfer_amount;
|
||||
$body["total_num"] = 1;
|
||||
$body["transfer_detail_list"] = [$detail->toArray()];
|
||||
|
||||
$sign = $this->signature('POST', '/v3/transfer/batches', $json = json_encode($body, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
$client = $this->createClient($sign, $json);
|
||||
$client->post('/v3/transfer/batches');
|
||||
$client->close();
|
||||
|
||||
$data = json_decode($client->getBody(), TRUE);
|
||||
if (json_last_error() != JSON_ERROR_NONE) {
|
||||
return ['code' => $client->getStatusCode(), 'message' => $client->getBody()];
|
||||
} else {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace wchat\wx\V3;
|
||||
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
|
||||
class TransferDetail
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @param string $out_detail_no
|
||||
* @param int|float $transfer_amount
|
||||
* @param string $transfer_remark
|
||||
* @param string $openid
|
||||
* @param string $user_name
|
||||
*/
|
||||
public function __construct(
|
||||
public string $out_detail_no,
|
||||
public int|float $transfer_amount,
|
||||
public string $transfer_remark,
|
||||
public string $openid,
|
||||
public string $user_name = ''
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
#[ArrayShape(['out_detail_no' => "string", 'transfer_amount' => "float|int", 'transfer_remark' => "string", 'openid' => "string", 'user_name' => "string"])]
|
||||
public function toArray(): array
|
||||
{
|
||||
if (empty($this->user_name)) {
|
||||
return [
|
||||
'out_detail_no' => $this->out_detail_no,
|
||||
'transfer_amount' => $this->transfer_amount,
|
||||
'transfer_remark' => $this->transfer_remark,
|
||||
'openid' => $this->openid,
|
||||
];
|
||||
}
|
||||
return [
|
||||
'out_detail_no' => $this->out_detail_no,
|
||||
'transfer_amount' => $this->transfer_amount,
|
||||
'transfer_remark' => $this->transfer_remark,
|
||||
'openid' => $this->openid,
|
||||
'user_name' => $this->user_name,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,7 +20,7 @@ class WxV3AppPayment extends SmallProgram
|
||||
* @return array
|
||||
* @throws
|
||||
*/
|
||||
public function payment(string $orderNo, int $total, string $openId = NULL, string $payer_client_ip = '127.0.0.1'): array
|
||||
public function payment(string $orderNo, int $total, ?string $openId = NULL, string $payer_client_ip = '127.0.0.1'): array
|
||||
{
|
||||
$body = $this->getInitCore($orderNo, $total);
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ class WxV3NativePayment extends SmallProgram
|
||||
* @throws
|
||||
*/
|
||||
#[ArrayShape(['code_url' => "string"])]
|
||||
public function payment(string $orderNo, int $total, string $openId = NULL, string $payer_client_ip = '127.0.0.1'): array
|
||||
public function payment(string $orderNo, int $total, ?string $openId = NULL, string $payer_client_ip = '127.0.0.1'): array
|
||||
{
|
||||
$body = $this->getInitCore($orderNo, $total);
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ class WxV3Payment extends SmallProgram
|
||||
* @return array
|
||||
* @throws
|
||||
*/
|
||||
public function payment(string $orderNo, int $total, string $openId = NULL, string $payer_client_ip = '127.0.0.1'): array
|
||||
public function payment(string $orderNo, int $total, ?string $openId = NULL, string $payer_client_ip = '127.0.0.1'): array
|
||||
{
|
||||
$body = $this->getInitCore($orderNo, $total);
|
||||
$body['payer'] = ['openid' => $openId];
|
||||
|
||||
@@ -54,19 +54,23 @@ class WxV3PaymentNotify extends SmallProgram
|
||||
public function verify(RequestInterface $request): bool
|
||||
{
|
||||
$platformPublicKeyInstance = $this->rsaFrom($this->payConfig->pay->wx->mchKey, KEY_TYPE_PUBLIC);
|
||||
$inWechatpaySignature = $request->getHeaderLine('Wechatpay-Signature'); // 请根据实际情况获取
|
||||
$inWechatpayTimestamp = $request->getHeaderLine('Wechatpay-Timestamp'); // 请根据实际情况获取
|
||||
$inWechatpayNonce = $request->getHeaderLine('Wechatpay-Nonce'); // 请根据实际情况获取
|
||||
$inWechatpaySignature = $request->getHeaderLine('wechatpay-signature'); // 请根据实际情况获取
|
||||
$inWechatpayTimestamp = $request->getHeaderLine('wechatpay-timestamp'); // 请根据实际情况获取
|
||||
$inWechatpayNonce = $request->getHeaderLine('wechatpay-nonce'); // 请根据实际情况获取
|
||||
$inBody = $request->getBody()->getContents(); // 请根据实际情况获取,例如: file_get_contents('php://input');
|
||||
$timeOffsetStatus = 300 >= abs(time() - (int)$inWechatpayTimestamp);
|
||||
$verifiedStatus = $this->notifyVerify(
|
||||
$this->lineFeed([$inWechatpayTimestamp, $inWechatpayNonce, $inBody]),
|
||||
$this->lineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
|
||||
$inWechatpaySignature,
|
||||
$platformPublicKeyInstance);
|
||||
|
||||
$this->decode($this->resource['ciphertext'], $this->resource['nonce'], $this->resource['associated_data']);
|
||||
|
||||
if (!$timeOffsetStatus || !$verifiedStatus) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return $this->decode($this->resource['ciphertext'], $this->resource['nonce'], $this->resource['associated_data']);
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +115,7 @@ class WxV3PaymentNotify extends SmallProgram
|
||||
*/
|
||||
protected function rsaFrom(string $thing, string $type = KEY_TYPE_PRIVATE): OpenSSLAsymmetricKey
|
||||
{
|
||||
$pkey = (($isPublic = $type === KEY_TYPE_PUBLIC) ? openssl_pkey_get_public(file_get_contents($thing)) : openssl_pkey_get_private(file_get_contents($thing)));
|
||||
$pkey = (($isPublic = $type === KEY_TYPE_PUBLIC) ? openssl_pkey_get_public('file://' . $thing) : openssl_pkey_get_private('file://' . $thing));
|
||||
if (false === $pkey) {
|
||||
throw new \UnexpectedValueException(sprintf('Cannot load %s from(%s), please take care about the $thing input.', $isPublic ? 'publicKey' : 'privateKey', gettype($thing)));
|
||||
}
|
||||
@@ -142,6 +146,9 @@ class WxV3PaymentNotify extends SmallProgram
|
||||
$this->notifyModel->attach = $data['attach'];
|
||||
$this->notifyModel->success_time = $data['success_time'];
|
||||
$this->notifyModel->promotion_detail = [];
|
||||
if (!isset($data['promotion_detail'])) {
|
||||
return true;
|
||||
}
|
||||
foreach ($data['promotion_detail'] as $datum) {
|
||||
$detail = new PromotionDetail();
|
||||
$detail->amount = $datum['amount'];
|
||||
@@ -154,8 +161,10 @@ class WxV3PaymentNotify extends SmallProgram
|
||||
$detail->currency = $datum['currency'];
|
||||
$detail->stock_id = $datum['stock_id'];
|
||||
$detail->goods_detail = [];
|
||||
foreach ($datum['goods_detail'] as $value) {
|
||||
$detail->goods_detail[] = new GoodsDetail($value);
|
||||
if (isset($data['goods_detail'])) {
|
||||
foreach ($datum['goods_detail'] as $value) {
|
||||
$detail->goods_detail[] = new GoodsDetail($value);
|
||||
}
|
||||
}
|
||||
$this->notifyModel->promotion_detail[] = $detail;
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ trait WxV3PaymentTait
|
||||
{
|
||||
$payConfig = $this->getPayConfig();
|
||||
|
||||
$mch_private_key = openssl_get_privatekey(file_get_contents($payConfig->pay->wx->mchKey));
|
||||
$mch_private_key = openssl_get_privatekey(file_get_contents($payConfig->pay->wx->mchCert));
|
||||
|
||||
openssl_sign($body, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');
|
||||
return base64_encode($raw_sign);
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace wchat\wx\V3;
|
||||
|
||||
use Exception;
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
use Kiri;
|
||||
use wchat\wx\SmallProgram;
|
||||
use wchat\wx\V3\Libs\TransferDetail;
|
||||
|
||||
class WxV3Transfer extends SmallProgram
|
||||
{
|
||||
use WxV3PaymentTait;
|
||||
|
||||
/**
|
||||
* @param TransferDetail $detail
|
||||
* @return array<'out_bill_no', 'transfer_bill_no', 'create_time', 'state'>
|
||||
* @throws Exception
|
||||
*/
|
||||
#[ArrayShape([])]
|
||||
public function transfer(TransferDetail $detail): array
|
||||
{
|
||||
$payConfig = $this->getPayConfig();
|
||||
$body = $detail->toArray();
|
||||
if ($payConfig->typeIsApp()) {
|
||||
$body['appid'] = $payConfig->pay->wx->appId;
|
||||
} else {
|
||||
$body['appid'] = $payConfig->appId;
|
||||
}
|
||||
$sign = $this->signature('POST', '/v3/fund-app/mch-transfer/transfer-bills', $json = json_encode($body, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
$client = $this->createClient($sign, $json);
|
||||
$client->post('/v3/fund-app/mch-transfer/transfer-bills');
|
||||
$client->close();
|
||||
|
||||
Kiri::getLogger()->println($client->getBody());
|
||||
|
||||
if ($client->getStatusCode() == 200) {
|
||||
return json_decode($client->getBody(), TRUE);
|
||||
}
|
||||
throw new Exception('转账申请发起失败');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -4,8 +4,8 @@ namespace wchat\wx\V3;
|
||||
|
||||
use Exception;
|
||||
use JetBrains\PhpStorm\ArrayShape;
|
||||
use Kiri\Client;
|
||||
use wchat\wx\SmallProgram;
|
||||
use wchat\wx\V3\Libs\TransferDetail;
|
||||
|
||||
class WxV3Withdrawal extends SmallProgram
|
||||
{
|
||||
@@ -66,4 +66,34 @@ class WxV3Withdrawal extends SmallProgram
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param TransferDetail $detail
|
||||
* @return array<'out_bill_no', 'transfer_bill_no', 'create_time', 'state'>
|
||||
* @throws Exception
|
||||
*/
|
||||
#[ArrayShape([])]
|
||||
public function transfer(TransferDetail $detail): array
|
||||
{
|
||||
$payConfig = $this->getPayConfig();
|
||||
$body = $detail->toArray();
|
||||
if ($payConfig->typeIsApp()) {
|
||||
$body['appid'] = $payConfig->pay->wx->appId;
|
||||
} else {
|
||||
$body['appid'] = $payConfig->appId;
|
||||
}
|
||||
$sign = $this->signature('POST', '/v3/fund-app/mch-transfer/transfer-bills', $json = json_encode($body, JSON_UNESCAPED_UNICODE));
|
||||
|
||||
$client = $this->createClient($sign, $json);
|
||||
$client->post('/v3/fund-app/mch-transfer/transfer-bills');
|
||||
$client->close();
|
||||
|
||||
Kiri::getLogger()->println($client->getBody());
|
||||
|
||||
if ($client->getStatusCode() == 200) {
|
||||
return json_decode($client->getBody(), TRUE);
|
||||
}
|
||||
throw new Exception('转账申请发起失败');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user