vrajroham / laravel-bitpay
laravel 的 BitPay 包装器
Requires
- php: ^7.4 || ^8.0 || ^8.1
- ext-json: *
- bitpay/sdk: ~7.1.0
Requires (Dev)
- phpunit/phpunit: >=9.5
README
LaravelBitPay 允许您和您的业务在 Laravel 应用程序内进行比特币、比特币现金和 10+ 其他 BitPay 支持的加密货币交易。
需要 PHP 7.4+
⚠️ 从 v4 迁移 ⚠️
如果从 v4 升级,请遵循 MIGRATION.md
支持的资源
内容
安装
安装包
您可以通过composer安装此包
composer require vrajroham/laravel-bitpay
发布配置文件
使用以下命令发布配置文件
php artisan vendor:publish --provider="Vrajroham\LaravelBitpay\LaravelBitpayServiceProvider"
这将创建一个位于您的 config 目录内的 laravel-bitpay.php
文件。
添加配置值
将以下密钥添加到您的 .env
文件中,并将值更新为您的偏好设置(了解更多配置信息)
BITPAY_PRIVATE_KEY_PATH= BITPAY_NETWORK=testnet BITPAY_KEY_STORAGE_PASSWORD=RandomPasswordForEncryption BITPAY_ENABLE_MERCHANT=true BITPAY_ENABLE_PAYOUT=false BITPAY_MERCHANT_TOKEN= BITPAY_PAYOUT_TOKEN=
生成密钥对和 API 令牌
laravel-bitpay:createkeypair
命令为每个启用的外观生成BitPay API令牌和配对码
php artisan laravel-bitpay:createkeypair
ℹ️ 默认情况下,该命令将使用位于
BITPAY_PRIVATE_KEY_PATH
的(有效)现有私钥。您可以通过指定--fresh
或-f
选项来显式生成一个新的私钥,从中派生令牌。
在成功生成API令牌后,您需要通过访问提供的链接来批准它。
⚠️ 请注意,在您批准和使用相关的API令牌之前,必须在您的BitPay商户账户上启用 payout
外观。这意味着您将无法对 收件人 和 支付 资源执行操作。要启用支付功能,请 联系BitPay支持。
配置 Webhooks(可选)
BitPay资源状态更新完全基于webhooks(IPNs)。LaravelBitPay能够完全自动处理webhook请求。每当收到来自BitPay服务器的webhook时,都会触发 BitpayWebhookReceived
事件。请采取以下步骤来配置您的应用程序以监听webhook
1. 设置您的 webhook 路由
在您的目标路由文件中解析 bitPayWebhook
路由宏(建议使用 web.php
)。该宏接受一个单一、可选的参数,即您希望接收BitPay webhook POST
请求的URI路径。如果没有提供,则默认为 'laravel-bitpay/webhook'
// ... your other 'web' routes Route::bitPayWebhook(); // https://example.com/laravel-bitpay/webhook // OR ... Route::bitPayWebhook('receive/webhooks/here'); // https://example.com/receive/webhooks/here
ℹ️ 要在您的应用程序的任何位置检索您创建的新webhook路由,请使用:
route('laravel-bitpay.webhook.capture')
LaravelBitPay还提供了自动将配置的webhook url填充到相关资源的便利性。具体来说,当
您可以通过在 laravel-bitpay.php
配置文件中的 auto_populate_webhook
数组中取消注释相应的条目来按资源启用此功能。
⚠️ 如果手动设置了值,很可能是通过在资源初始化期间通过 $resource->setNotificationURL('https://...')
执行,则自动填充会被覆盖。
2. 设置您的 webhook 监听器
首先生成一个事件监听器
php artisan make:listener BitPayWebhookListener --event=\Vrajroham\LaravelBitpay\Events\BitpayWebhookReceived
然后,在生成的监听器的 handle(...)
函数中实现您应用程序特定的逻辑。
在以下示例中,我们假设您已经 创建了一个发票,并将其 token
存储在您的内部 Order
模型中
/** * Handle the webhook event, keeping in mind that the server doesn't trust the client (us), so neither should * we trust the server. Well, trust, but verify. * * @param BitpayWebhookReceived $event * @return void */ public function handle(BitpayWebhookReceived $event) { // Extract event payload $payload = $event->payload; // Verify that webhook is for a BitPay Invoice resource if (in_array($payload['event']['code'], array_keys(BitPayConstants::INVOICE_WEBHOOK_CODES))) { try { // Do not trust the webhook data. Pull the referenced Invoice from BitPay's server $invoice = LaravelBitpay::getInvoice($payload['data']['id']); // Now grab our internal Order instance for this supposed Invoice $order = Order::whereOrderId($invoice->getOrderId())->first(); // Verify Invoice token, previously stored at time of creation // Learn more at: https://github.com/vrajroham/laravel-bitpay#create-an-invoice if ($invoice->getToken() !== $order->invoice_token) { return; } $invoice_status = $invoice->getStatus(); // Do something about the new Invoice status if ($invoice_status === InvoiceStatus::Paid) { $order->update(['status' => $invoice_status]) && OrderStatusChanged::dispatch($order->refresh()); } } catch (BitPayException $e) { Log::error($e); } } }
最后,将您的监听器映射到 EventServiceProvider
的 $listen
数组中的 BitpayWebhookReceived
事件
/** * The event listener mappings for the application. * * @var array */ protected $listen = [ // ... other event-listener mappings BitpayWebhookReceived::class => [ BitPayWebhookListener::class, ], ]
示例
发票
发票是对特定买家的时间敏感的支付请求。发票有一个固定的价格,通常以法定货币表示。它还有一个由BitPay计算的支持的加密货币的等价价格,按照锁定汇率计算,有效期为15分钟。
创建发票
在这个示例中,我们假设您已经创建了一个与该发票(以下简称 $order
)关联的等效 Order
模型实例。
// Create instance of Invoice $invoice = LaravelBitpay::Invoice(449.99, Currency::USD); // Always use the BitPay Currency model to prevent typos // Set item details (Only 1 item per Invoice) $invoice->setItemDesc('You "Joe Goldberg" Life-Size Wax Figure'); $invoice->setItemCode('sku-1234'); $invoice->setPhysical(true); // Set to false for digital/virtual items // Ensure you provide a unique OrderId for each Invoice $invoice->setOrderId($order->order_id); // Create Buyer Instance $buyer = LaravelBitpay::Buyer(); $buyer->setName('John Doe'); $buyer->setEmail('john.doe@example.com'); $buyer->setAddress1('2630 Hegal Place'); $buyer->setAddress2('Apt 42'); $buyer->setLocality('Alexandria'); $buyer->setRegion('VA'); $buyer->setPostalCode(23242); $buyer->setCountry('US'); $buyer->setNotify(true); // Instructs BitPay to email Buyer about their Invoice // Attach Buyer to Invoice $invoice->setBuyer($buyer); // Set URL that Buyer will be redirected to after completing the payment, via GET Request $invoice->setRedirectURL(route('your-bitpay-success-url')); // Set URL that Buyer will be redirected to after closing the invoice or after the invoice expires, via GET Request $invoice->setCloseURL(route('your-bitpay-cancel-url')); $invoice->setAutoRedirect(true); // Optional. Learn more at: https://github.com/vrajroham/laravel-bitpay#1-setup-your-webhook-route $invoice->setNotificationUrl('https://example.com/your-custom-webhook-url'); // This is the recommended IPN format that BitPay advises for all new implementations $invoice->setExtendedNotifications(true); // Create invoice on BitPay's server $invoice = LaravelBitpay::createInvoice($invoice); $invoiceId = $invoice->getId(); $invoiceToken = $invoice->getToken(); // You should save Invoice ID and Token, for your reference $order->update(['invoice_id' => $invoiceId, 'invoice_token' => $invoiceToken]); // Redirect user to the Invoice's hosted URL to complete payment $paymentUrl = $invoice->getUrl(); return Redirect::to($paymentUrl);
ℹ️ 强烈建议您将发票 ID 和令牌存储在您的内部模型(们)中。令牌在验证网络钩子时可能会很有用。
检索现有发票
$invoice = LaravelBitpay::getInvoice('invoiceId_sGsdVsgheF');
检索现有发票列表
在这个示例中,我们检索所有 MTD(月度发票)。
$startDate = date('Y-m-d', strtotime('first day of this month')); $endDate = date('Y-m-d'); $invoices = LaravelBitpay::getInvoices($startDate, $endDate);
请求重新发送发票网络钩子。
// True if the webhook has been resent for the current invoice status, false otherwise. $webhookResent = LaravelBitpay::requestInvoiceWebhook('invoiceId_sGsdVsgheF');
退款
退款请求是与发票关联的完整退款或部分退款。全额付款的发票可以通过商家授权发出退款,而欠款和超付款的发票则由 BitPay 自动执行,向客户发出欠款或超付款金额。
退款发票
Jane 购买的商品到货时已损坏。请退还女士她的加密货币。
$invoice = LaravelBitpay::getInvoice('invoiceId_sGsdVsgheF'); $refundRequested = LaravelBitpay::createRefund($invoice, 'jane.doe@example.com', 0.016, 'ETH'); if ($refundRequested) { // Don't just sit there. Do something! }
检索退款请求
让我们定期检索(并检查)Jane 的退款请求状态。
$invoice = LaravelBitpay::getInvoice('invoiceId_sGsdVsgheF'); $refund = LaravelBitpay::getRefund($invoice, 'refundId_pUdhjwGjsg');
检索发票上的所有退款请求
在这个示例中,我们检索与 Jane 发票相关的所有退款请求。
$invoice = LaravelBitpay::getInvoice('invoiceId_sGsdVsgheF'); $refundRequests = LaravelBitpay::getRefunds($invoice);
取消退款请求
结果发现 Jane 最初没有遵循说明书。商品可以正常使用,她不再想要退款。
$invoice = LaravelBitpay::getInvoice('invoiceId_sGsdVsgheF'); $refundRequestCancelled = LaravelBitpay::cancelRefund($invoice->getId(), 'refundId_pUdhjwGjsg');
账单
账单是针对特定买家的付款请求。账单行项目具有固定价格,通常以法定货币标价。
创建账单
在以下示例中,我们创建了一个10天后到期的账单。
// Initialize Bill $billData = LaravelBitpay::Bill(); $billData->setNumber('bill1234-EFGH'); $billData->setCurrency(Currency::USD); // Always use the BitPay Currency model to prevent typos $dueDate = date(BitPayConstants::DATETIME_FORMAT, strtotime('+10 days')); // ISO-8601 formatted date $billData->setDueDate($dueDate); $billData->setPassProcessingFee(true); // Let the recipient shoulder BitPay's processing fee // Prepare Bill recipient's data $billData->setName('John Doe'); $billData->setAddress1('2630 Hegal Place'); $billData->setAddress2('Apt 42'); $billData->setCity('Alexandria'); $billData->setState('VA'); $billData->setZip(23242); $billData->setCountry('US'); $billData->setEmail('john.doe@example.com'); $billData->setCc(['jane.doe@example.com']); $billData->setPhone('555-123-456'); // Prepare Bill's line item(s) $itemUno = LaravelBitpay::BillItem(); $itemUno->setDescription('Squid Game "Front Man" Costume'); $itemUno->setPrice(49.99); $itemUno->setQuantity(2); $itemDos = LaravelBitpay::BillItem(); $itemDos->setDescription('GOT "House Stark" Sterling Silver Pendant'); $itemDos->setPrice(35); $itemDos->setQuantity(1); $billData->setItems([$itemUno, $itemDos]); // Create Bill $bill = LaravelBitpay::createBill($billData); // Store the Bill's BitPay ID and URL for future reference $billId = $bill->getId(); $billPaymentUrl = $bill->getUrl(); // OR // Redirect the recipient to BitPay's hosted Bill payment page Redirect::to($billPaymentUrl);
检索账单
$bill = LaravelBitpay::getBill('bill1234-EFGH');
检索现有账单列表
您可以通过指定账单状态来缩小检索的列表。
$paidBills = LaravelBitpay::getBills(BillStatus::Paid);
更新账单
我们成功向客户推销了一款产品。让我们向他们现有的账单添加一个额外的行项目。
$existingBill = LaravelBitpay::getBill('bill1234-EFGH'); $existingItems = $existingBill->getItems(); $billData = LaravelBitpay::Bill(); $billData->setId($existingBill->getId()); $itemTres = LaravelBitpay::BillItem(); $itemTres->setDescription('The Tomorrow War "White Spike" Life-Size Wax Figure'); $itemTres->setPrice(189.99); $itemTres->setQuantity(1); $billData->setItems(array_merge($existingItems, [$itemTres])); // Update Bill $updatedBill = LaravelBitpay::updateBill($billData, $billData->getId());
通过电子邮件发送账单
$bill = LaravelBitpay::getBill('bill1234-EFGH'); $billDelivered = LaravelBitpay::deliverBill($bill->getId(), $bill->getToken()); if ($billDelivered) { // Bill delivered successfully. Do something about that... or not. }
订阅
订阅是与特定买家重复计费协议。BitPay 根据指定的计划向活跃订阅中指定的买家发送账单电子邮件。
创建订阅
让我们创建一个每月28日交付并在下一个月的第一天上午9点到期的订阅。
// Initialize Subscription $subscriptionData = LaravelBitpay::Subscription(); $subscriptionData->setSchedule(BitPayConstants::SUBSCRIPTION_SCHEDULE_MONTHLY); // Optional recurring bill data $billData = [ 'number' => 'subscription1234-ABCD', 'name' => 'John Doe', 'address1' => '2630 Hegal Place', 'address2' => 'Apt 42', 'city' => 'Alexandria', 'state' => 'VA', 'zip' => 23242, 'country' => 'US', 'cc' => ['jane.doe@example.com'], 'phone' => '555-123-456', 'passProcessingFee' => true, ]; $dueDate = date(BitPayConstants::DATETIME_FORMAT, strtotime('first day of next month 9 AM')); $billItems = array( LaravelBitpay::SubscriptionItem(100.00, 1, 'Web Hosting - 4 CPUs | 16GB Memory | 400GB SSD'), LaravelBitpay::SubscriptionItem(80.00, 1, 'Basic Website Maintenance'), ); // Autofill optional bill data $mapper = new JsonMapper(); $billData = $mapper->map( $billData, LaravelBitpay::BillData( Currency::USD, // Always use the BitPay Currency model to prevent typos 'john.doe@example.com', $dueDate, $billItems ) ); $subscriptionData->setBillData($billData); // A little wizardry to always get the 28th day of the current month (leap year safe) $deliveryDate = strtotime('first day of this month 9 AM'); $deliveryDate = new \DateTime("@$deliveryDate"); $deliveryDate = $deliveryDate->modify('+27 days')->getTimestamp(); $deliveryDate = date(BitPayConstants::DATETIME_FORMAT, $deliveryDate); $subscriptionData->setNextDelivery($deliveryDate); // Create the Subscription on BitPay $subscription = LaravelBitpay::createSubscription($subscriptionData); // You may then store the Subscription ID for future reference $subscriptionId = $subscription->getId();
检索订阅
$subscription = LaravelBitpay::getSubscription('6gqe8y5mkc5Qx2a9zmspgx');
检索现有订阅列表
您可以通过指定订阅状态来缩小检索的列表。
$activeSubscriptions = LaravelBitpay::getSubscriptions(SubscriptionStatus::Active);
更新订阅
在这个示例中,我们通过更新其状态来激活订阅。
$subscriptionData = LaravelBitpay::Subscription(); $subscriptionData->setId('6gqe8y5mkc5Qx2a9zmspgx'); $subscriptionData->setStatus(SubscriptionStatus::Active); $activatedSubscription = LaravelBitpay::updateSubscription($subscriptionData, $subscriptionData->getId());
结算
结算是将支付利润从 BitPay 转移到商家、合作伙伴等拥有的银行账户和加密货币钱包的过程。
检索结算
在这个示例中,我们检索以欧元(EUR)计价的已完成 YTD(年度至今)结算。我们只需要前100条记录中的第5条。
$startDate = date('Y-m-d', strtotime('first day of this year')); $endDate = date('Y-m-d'); $eurSettlements = LaravelBitpay::getSettlements( Currency::EUR, $startDate, $endDate, BitPayConstants::SETTLEMENT_STATUS_COMPLETED, 100, 4 );
检索结算
$settlement = LaravelBitpay::getSettlement('settlementId_uidwb3668');
获取对账报告
对账报告是对结算期内活动的一份详细报告,以便对 BitPay 的到账结算进行对账。
$settlement = LaravelBitpay::getSettlement('settlementId_uidwb3668'); $settlementReport = LaravelBitpay::getSettlementReconciliationReport($settlement);
账本
账本是货币流动的记录。
检索账户余额
$accountBalances = LaravelBitpay::getLedgers();
检索账本条目
在这个示例中,我们检索以美元(USD)计价的 MTD(月度至今)账目。
$startDate = date('Y-m-d', strtotime('first day of this month')); $endDate = date('Y-m-d'); $usdLedgerEntries = LaravelBitpay::getLedger(Currency::USD, $startDate, $endDate);
收款人
收款人资源允许商家邀请他们的客户注册 BitPay 个人账户。
⚠️ 您的 BitPay 商户账户必须获得付款功能授权。要启用付款功能,请联系 BitPay 支持。
邀请收款人
// Init individual recipients $jane = LaravelBitpay::PayoutRecipient('jane.doe@example.com', 'Plain Jane'); $ada = LaravelBitpay::PayoutRecipient('ada@cardano.org', 'Ada Lovelace'); // Optional. Learn more at https://github.com/vrajroham/laravel-bitpay#1-setup-your-webhook-route $ada->setNotificationUrl('https://example.com/your-custom-webhook-url'); // Batch all individual recipients $recipients = LaravelBitpay::PayoutRecipients([$jane, $ada]); // Submit invites $recipientsInvited = LaravelBitpay::invitePayoutRecipients($recipients); // Do something with the returned invitees foreach ($recipientsInvited as $recipient) { $recipientId = $recipient->getId(); $recipientToken = $recipient->getToken(); // ... store Recipient ID and Token somewhere persistent // Perform other desired actions \App\Events\LookOutForAnInviteEmail::dispatch($recipient->getEmail()); }
ℹ️ 强烈建议您将收款人 ID 和令牌存储在您的内部模型(们)中。令牌在验证网络钩子时可能会很有用。
检索收款人
$recipient = LaravelBitpay::getPayoutRecipient('recipientId_adaLovelace')
按状态检索收款人
在这个示例中,我们检索了100个(从第50个开始)已通过良好旧式 Onfido 身份验证检查的收款人。
$verifiedRecipients = LaravelBitpay::getPayoutRecipients(RecipientStatus::VERIFIED, 100, 49);
更新收款人
$recipient = LaravelBitpay::getPayoutRecipient('recipientId_adaLovelace'); $recipient->setLabel('Cardano To The Moon'); $updatedRecipient = LaravelBitpay::updatePayoutRecipient($recipient->getId(), $recipient);
删除收款人
$recipientRemoved = LaravelBitpay::removePayoutRecipient('recipientId_janeDoe');
请求重新发送收款人 webhook
// True if the webhook has been resent for the current recipient status, false otherwise. $webhookResent = LaravelBitpay::requestPayoutRecipientWebhook('recipientId_adaLovelace');
付款
付款是向员工、客户、合作伙伴等个人(或批量)比特币支付。
⚠️ 您的 BitPay 商户账户必须获得付款功能授权。要启用付款功能,请联系 BitPay 支持。
创建付款
假设 Ada Lovelace 接受了我们的邀请。在这个示例中,我们为她安排了一个个人付款,以奖励她从推荐中获得的5星评价。
// Initialize a Payout // Pay Ada in USD and record it on the BTC ledger $payoutData = LaravelBitpay::Payout(50.00, Currency::USD, Currency::BTC); // Set Payout details $payoutData->setRecipientId('recipientId_adaLovelace'); // From previously invited Recipient $payoutData->setReference('1234'); // Uniquely identifies an equivalent payout entry in your system $payoutData->setLabel('5-Star Bonus Affiliate Payment #1234 for Dec 2021'); $payoutData->setEffectiveDate('2021-12-31'); // Optional. Learn more at https://github.com/vrajroham/laravel-bitpay#1-setup-your-webhook-route $payoutData->setNotificationURL('https://example.com/your-custom-webhook-url'); // Create Payout on BitPay's server $payout = LaravelBitpay::createPayout($payoutData); $payoutId = $payout->getId(); $payoutToken = $payout->getToken(); // ... store Payout ID and Token somewhere persistent
ℹ️ 强烈建议您将付款 ID 和令牌存储在您的内部模型(们)中。令牌在验证网络钩子时可能会很有用。
创建付款批次
让我们支付我们两位顶级合作伙伴的辛勤工作,将这两笔付款合并为一个 API 调用,以提高效率。
// Initialize a Payout Batch $payoutBatchData = LaravelBitpay::PayoutBatch(Currency::USD); // Pay recipients in USD $payoutBatchData->setLedgerCurrency(Currency::ETH); // Record the payout batch on the ETH ledger $payoutBatchData->setAmount(500.00); $payoutBatchData->setReference('Aff_Jan-Feb_2022'); // Uniquely identifies an equivalent payout batch in your system $payoutBatchData->setLabel('Affiliate Payments for Jan-Feb 2022'); $payoutBatchData->setEffectiveDate('2022-02-28'); // Optional. Learn more at https://github.com/vrajroham/laravel-bitpay#1-setup-your-webhook-route $payoutBatchData->setNotificationURL('https://example.com/your-custom-webhook-url'); // Define Instruction(s) $payJane = LaravelBitpay::PayoutInstruction( 250.00, RecipientReferenceMethod::RECIPIENT_ID, 'recipientId_janeDoe' ); $payJane->setLabel('Affiliate Payment #1234 for Jan-Feb 2022'); $payAda = LaravelBitpay::PayoutInstruction( 250.00, RecipientReferenceMethod::RECIPIENT_ID, 'recipientId_adaLovelace' ); $payAda->setLabel('Affiliate Payment #5678 for Jan-Feb 2022'); // Attach Instruction(s) to Payout Batch $payoutBatchData->setInstructions([$payJane, $payAda]); // Create Payout Batch on BitPay's server $payoutBatch = LaravelBitpay::createPayoutBatch($payoutBatchData); $payoutBatchId = $payoutBatch->getId(); $payoutBatchToken = $payoutBatch->getToken(); // ... store Payout Batch ID and Token somewhere persistent
ℹ️ 强烈建议您将付款批次 ID 和令牌存储在您的内部模型(们)中。令牌在验证网络钩子时可能会很有用。
检索付款
$payout = LaravelBitpay::getPayout('payoutId_jws43dbnfpg');
检索付款批次
$payoutBatch = LaravelBitpay::getPayoutBatch('payoutBatchId_jws43dbnfpg');
根据状态检索付款
在这个示例中,我们检索所有完成的本年至今(YTD)付款。
$startDate = date('Y-m-d', strtotime('first day of this year')); $endDate = date('Y-m-d'); $completedPayouts = LaravelBitpay::getPayouts($startDate, $endDate, PayoutStatus::Complete);
根据状态检索付款批次
在这个示例中,我们检索所有已取消的本年至今(YTD)付款批次。
$startDate = date('Y-m-d', strtotime('first day of this year')); $endDate = date('Y-m-d'); $cancelledPayoutBatches = LaravelBitpay::getPayoutBatches($startDate, $endDate, PayoutStatus::Cancelled);
取消付款
$payoutCancelled = LaravelBitpay::cancelPayout('payoutId_jws43dbnfpg');
取消付款批次
$payoutBatchCancelled = LaravelBitpay::cancelPayoutBatch('payoutBatchId_jws43dbnfpg');
请求重新发送付款 webhook
// True if the webhook has been resent for the current payout status, false otherwise. $webhookResent = LaravelBitpay::requestPayoutWebhook('payoutId_jws43dbnfpg');
请求重新发送付款批次 webhook
// True if the webhook has been resent for the current payout batch status, false otherwise. $webhookResent = LaravelBitpay::requestPayoutBatchWebhook('payoutBatchId_jws43dbnfpg');
货币
货币是BitPay支持的法定货币。
检索支持的货币
在这个示例中,我们检索支持BitPay货币对象的列表。
$supportedCurrencies = LaravelBitpay::getCurrencies();
汇率
汇率是兑换率,表示一比特币相当于多少法定货币单位。
获取BitPay维护的汇率表
$rates = LaravelBitpay::getRates(); $btcToUsdRate = $rates->getRate(Currency::USD); // Always use the BitPay Currency model to prevent typos
获取特定加密货币的所有汇率
$ethRates = LaravelBitpay::getCurrencyRates(Currency::ETH); $ethToUsdRate = $ethRates->getRate(Currency::USD);
获取加密货币/法定货币对的汇率
$dogeToUsdRate = LaravelBitpay::getCurrencyPairRate(Currrency::DOGE, Currency::USD);
测试
composer test
更新日志
有关最近更改的更多信息,请参阅变更日志。
贡献
有关详细信息,请参阅贡献指南。
安全
如果您发现任何与安全相关的问题,请通过电子邮件vaibhavraj@vrajroham.me或iamalexstewart@gmail.com联系,而不是使用问题跟踪器。
致谢
许可协议
MIT许可(MIT)。有关更多信息,请参阅许可文件。