{"id":21373575,"url":"https://github.com/itenfay/dyfstore","last_synced_at":"2025-10-10T06:34:15.819Z","repository":{"id":56908094,"uuid":"192742734","full_name":"itenfay/DYFStore","owner":"itenfay","description":"iOS内购(This is used for in app purchases on iOS)","archived":false,"fork":false,"pushed_at":"2024-11-11T05:21:30.000Z","size":446,"stargazers_count":68,"open_issues_count":1,"forks_count":9,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-24T12:29:38.328Z","etag":null,"topics":["buy","iap","in-app-purchase","in-app-purchases","ios","pay","payment","purchase","receipt","receipt-url","receipt-validation","receipt-verification","receipt-verifier","skpayment","skpaymentqueue","skpaymenttransaction","skproduct","storekit","swift","transaction"],"latest_commit_sha":null,"homepage":"https://www.jianshu.com/p/de030cd6e4a3","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/itenfay.png","metadata":{"files":{"readme":"README-en.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-06-19T13:55:45.000Z","updated_at":"2025-08-23T18:11:03.000Z","dependencies_parsed_at":"2024-06-02T10:13:56.165Z","dependency_job_id":"26617c6e-14db-418b-9e6d-113314537dd8","html_url":"https://github.com/itenfay/DYFStore","commit_stats":{"total_commits":50,"total_committers":3,"mean_commits":"16.666666666666668","dds":"0.21999999999999997","last_synced_commit":"76e298bd9bac69e2ebc7210a5f8339d8378883eb"},"previous_names":["itenfay/dyfstore","chenxing640/dyfstore"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/itenfay/DYFStore","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itenfay%2FDYFStore","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itenfay%2FDYFStore/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itenfay%2FDYFStore/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itenfay%2FDYFStore/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/itenfay","download_url":"https://codeload.github.com/itenfay/DYFStore/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itenfay%2FDYFStore/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279002962,"owners_count":26083489,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-10T02:00:06.843Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["buy","iap","in-app-purchase","in-app-purchases","ios","pay","payment","purchase","receipt","receipt-url","receipt-validation","receipt-verification","receipt-verifier","skpayment","skpaymentqueue","skpaymenttransaction","skproduct","storekit","swift","transaction"],"created_at":"2024-11-22T08:29:21.227Z","updated_at":"2025-10-10T06:34:15.801Z","avatar_url":"https://github.com/itenfay.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"[中文版](README.md) | **English Version**\n\n\n## DYFStore\n\nA lightweight and easy-to-use iOS library for In-App Purchases ([Objective-C Version](https://github.com/itenfay/DYFStoreKit)).\n\n`DYFStore` uses blocks and [notifications](#Notifications) to wrap `StoreKit`, provides [receipt verification](#Receipt-verification) and [transaction persistence](#Transaction-persistence). \n\n[![License MIT](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](LICENSE)\u0026nbsp;\n[![CocoaPods](http://img.shields.io/cocoapods/v/DYFStore.svg?style=flat)](http://cocoapods.org/pods/DYFStore)\u0026nbsp;\n![CocoaPods](http://img.shields.io/cocoapods/p/DYFStore.svg?style=flat)\u0026nbsp;\n\n\n## Related Links\n\n- [DYFSwiftRuntimeProvider](https://github.com/itenfay/DYFSwiftRuntimeProvider/)\n- [DYFSwiftKeychain](https://github.com/itenfay/DYFSwiftKeychain/)\n- [DYFStoreReceiptVerifier_Swift](https://github.com/itenfay/DYFStoreReceiptVerifier_Swift/)\n- [Unity-iOS-InAppPurchase](https://github.com/itenfay/Unity-iOS-InAppPurchase/)\n- [in-app-purchase-complete-programming-guide-for-iOS](https://itenfay.github.io/2016/10/16/in-app-purchase-complete-programming-guide-for-iOS/)\n- [how-to-easily-complete-in-app-purchase-configuration-for-iOS](https://itenfay.github.io/2016/10/12/how-to-easily-complete-in-app-purchase-configuration-for-iOS/)\n\n\n## Features\n\n- Super simple in-app purchases.\n- Built-in support for remembering your purchases.\n- Built-in receipt validation (remote).\n- Built-in hosted content downloads and notifications.\n\n\n## Group (ID:614799921)\n\n\u003cdiv align=left\u003e\n\u0026emsp; \u003cimg src=\"https://github.com/itenfay/DYFStore/raw/master/images/g614799921.jpg\" width=\"30%\" /\u003e\n\u003c/div\u003e\n\n\n## Installation\n\nUsing [CocoaPods](https://cocoapods.org):\n\n``` \npod 'DYFStore'\n```\n\nOr\n\n```\npod 'DYFStore', '~\u003e 2.3.0'\n```\n\nCheck out the [wiki](https://github.com/itenfay/DYFStore/wiki/Installation) for more options.\n\n\n## Usage\n\nNext I'll show you how to use `DYFStore`.\n\n### Initialization\n\nThe initialization is as follows.\n\n- Whether to allow the logs output to the console, set 'true' in debug mode, view the logs of the whole process of in-app purchase, and set 'false' when publishing app in release mode.\n- Adds the observer of transactions and monitors the change of transactions.\n- Instantiates data persistent object and stores the related information of transactions.\n- Follows the agreement `DYFStoreAppStorePaymentDelegate` and processes payments for products purchased from the App Store.\n\n```\nfunc application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -\u003e Bool {\n\n    self.initIAPSDK()\n\n    return true\n}\n\nfunc initIAPSDK() {\n    SKIAPManager.shared.addStoreObserver()\n    \n    // Wether to allow the logs output to console.\n    DYFStore.default.enableLog = true\n    \n    // Adds an observer that responds to updated transactions to the payment queue.\n    // If an application quits when transactions are still being processed, those transactions are not lost. The next time the application launches, the payment queue will resume processing the transactions. Your application should always expect to be notified of completed transactions.\n    // If more than one transaction observer is attached to the payment queue, no guarantees are made as to the order they will be called in. It is recommended that you use a single observer to process and finish the transaction.\n    DYFStore.default.addPaymentTransactionObserver()\n    \n    // Sets the delegate processes the purchase which was initiated by user from the App Store.\n    DYFStore.default.delegate = self\n}\n```\n\nYou can process the purchase which was initiated by user from the App Store and provide your own implementation using the `DYFStoreAppStorePaymentDelegate` protocol:\n\n```\n// Processes the purchase which was initiated by user from the App Store.\nfunc didReceiveAppStorePurchaseRequest(_ queue: SKPaymentQueue, payment: SKPayment, forProduct product: SKProduct) -\u003e Bool {\n    if !DYFStore.canMakePayments() {\n        self.sk_showTipsMessage(\"Your device is not able or allowed to make payments!\")\n        return false\n    }\n    \n    // Get account name from your own user system.\n    let accountName = \"Handsome Jon\"\n    // This algorithm is negotiated with server developer.\n    let userIdentifier = accountName.tx_sha256 ?? \"\"\n    DYFStoreLog(\"userIdentifier: \\(userIdentifier)\")\n    SKIAPManager.shared.addPayment(product.productIdentifier, userIdentifier: userIdentifier)\n    return true\n}\n```\n\n### Request products\n\nYou need to check whether the device is not able or allowed to make payments before requesting products.\n\n```\nif !DYFStore.canMakePayments() {\n    self.sk_showTipsMessage(\"Your device is not able or allowed to make payments!\")\n    return\n}\n```\n\nTo begin the purchase process, your app must know its product identifiers. There are two strategies for retrieving information about the products from the App Store.\n\n**Strategy 1:** Your app can uses a product identifier to fetch information about product available for sale in the App Store and to submit payment request directly.\n\n```\n@IBAction func fetchesProductAndSubmitsPayment(_ sender: Any) {\n    // You need to check whether the device is not able or allowed to make payments before requesting product.\n    if !DYFStore.canMakePayments() {\n        self.sk_showTipsMessage(\"Your device is not able or allowed to make payments!\")\n        return\n    }\n    self.sk_showLoading(\"Loading...\")\n    \n    let productId = \"com.hncs.szj.coin42\"\n    DYFStore.default.requestProduct(withIdentifier: productId, success: { (products, invalidIdentifiers) in\n        self.sk_hideLoading()\n        if products.count == 1 {\n            let productId = products[0].productIdentifier\n            self.addPayment(productId)\n        } else {\n            self.sk_showTipsMessage(\"There is no this product for sale!\")\n        }\n    }) { (error) in\n        self.sk_hideLoading()\n        let value = error.userInfo[NSLocalizedDescriptionKey] as? String\n        let msg = value ?? \"\\(error.localizedDescription)\"\n        self.sendNotice(\"An error occurs, \\(error.code), \" + msg)\n    }\n}\n\nprivate func addPayment(_ productId: String) {\n    // Get account name from your own user system.\n    let accountName = \"Handsome Jon\"\n    // This algorithm is negotiated with server developer.\n    let userIdentifier = accountName.tx_sha256 ?? \"\"\n    DYFStoreLog(\"userIdentifier: \\(userIdentifier)\")\n    SKIAPManager.shared.addPayment(productId, userIdentifier: userIdentifier)\n}\n```\n\n**Strategy 2:** It can retrieve information about the products from the App Store and present its store UI to the user. Every product sold in your app has a unique product identifier. Your app uses these product identifiers to fetch information about products available for sale in the App Store, such as pricing, and to submit payment requests when users purchase those products.\n\n```\nfunc fetchProductIdentifiersFromServer() -\u003e [String] {\n    let productIds = [\n        \"com.hncs.szj.coin42\",   // 42 gold coins for ￥6.\n        \"com.hncs.szj.coin210\",  // 210 gold coins for ￥30.\n        \"com.hncs.szj.coin686\",  // 686 gold coins for ￥98.\n        \"com.hncs.szj.coin1386\", // 1386 gold coins for ￥198.\n        \"com.hncs.szj.coin2086\", // 2086 gold coins for ￥298.\n        \"com.hncs.szj.coin4886\", // 4886 gold coins for ￥698.\n        \"com.hncs.szj.vip1\",     // non-renewable vip subscription for a month.\n        \"com.hncs.szj.vip2\"      // Auto-renewable vip subscription for three months.\n    ]\n    return productIds\n}\n\n@IBAction func fetchesProductsFromAppStore(_ sender: Any) {\n    // You need to check whether the device is not able or allowed to make payments before requesting products.\n    if !DYFStore.canMakePayments() {\n        self.sk_showTipsMessage(\"Your device is not able or allowed to make payments!\")\n        return\n    }\n    self.sk_showLoading(\"Loading...\")\n    \n    let productIds = fetchProductIdentifiersFromServer()\n    DYFStore.default.requestProduct(withIdentifiers: productIds, success: { (products, invalidIdentifiers) in\n        self.sk_hideLoading()\n        if products.count \u003e 0 {\n            self.processData(products)\n        } else if products.count == 0 \u0026\u0026\n                    invalidIdentifiers.count \u003e 0 {\n            // Please check the product information you set up.\n            self.sk_showTipsMessage(\"There are no products for sale!\")\n        }\n    }) { (error) in\n        self.sk_hideLoading()\n        let value = error.userInfo[NSLocalizedDescriptionKey] as? String\n        let msg = value ?? \"\\(error.localizedDescription)\"\n        self.sendNotice(\"An error occurs, \\(error.code), \" + msg)\n    }\n}\n\nprivate func processData(_ products: [SKProduct]) {\n    var modelArray = [SKStoreProduct]()\n    for product in products {\n        let p = SKStoreProduct()\n        p.identifier = product.productIdentifier\n        p.name = product.localizedTitle\n        p.price = product.price.stringValue\n        p.localePrice = DYFStore.default.localizedPrice(ofProduct: product)\n        p.localizedDescription = product.localizedDescription\n        modelArray.append(p)\n    }\n    self.displayStoreUI(modelArray)\n}\n\nprivate func displayStoreUI(_ dataArray: [SKStoreProduct]) {\n    let storeVC = DYFStoreViewController()\n    storeVC.dataArray = dataArray\n    self.navigationController?.pushViewController(storeVC, animated: true)\n}\n```\n\n### Add payment\n\nRequests payment of the product with the given product identifier.\n\n```\nDYFStore.default.purchaseProduct(\"com.hncs.szj.coin210\")\n```\n\nIf you need an opaque identifier for the user’s account on your system to add payment, you can use a one-way hash of the user’s account name to calculate the value for this property.\n\nCalculates the SHA256 hash function:\n\n```\nextension String {\n    /// Custom method to calculate the SHA-256 hash using Common Crypto.\n    ///\n    /// - Parameter s: A string to calculate hash.\n    /// - Returns: A SHA-256 hash value string.\n    public var tx_sha256 : String? {\n        guard let cStr = cString(using: String.Encoding.utf8) else {\n            return nil\n        }\n        let digestLength = Int(CC_SHA256_DIGEST_LENGTH) // 32\n        let cStrLen = Int(lengthOfBytes(using: String.Encoding.utf8))\n        \n        // Confirm that the length of C string is small enough\n        // to be recast when calling the hash function.\n        if cStrLen \u003e UINT32_MAX {\n            print(\"C string too long to hash: \\(self)\")\n            return nil\n        }\n        \n        let md = UnsafeMutablePointer\u003cCUnsignedChar\u003e.allocate(capacity: digestLength)\n        CC_SHA256(cStr, CC_LONG(cStrLen), md)\n        // Convert the array of bytes into a string showing its hex represention.\n        let hash = NSMutableString()\n        for i in 0..\u003cdigestLength {\n            // Add a dash every four bytes, for readability.\n            if i != 0 \u0026\u0026 i%4 == 0 {\n                //hash.append(\"-\")\n            }\n            hash.appendFormat(\"%02x\", md[i])\n        }\n        md.deallocate()\n        \n        return hash as String\n    }\n}\n```\n\nRequests payment of the product with the given product identifier, an opaque identifier for the user’s account on your system.\n\n```\nDYFStore.default.purchaseProduct(\"com.hncs.szj.coin210\", userIdentifier: \"A43512564ACBEF687924646CAFEFBDCAEDF4155125657\")\n```\n\n### Restore transactions\n\n- Restores transactions without the user account identifier.\n\n```\nDYFStore.default.restoreTransactions()\n```\n\n- Restores transactions with the user account identifier.\n\n```\nDYFStore.default.restoreTransactions(userIdentifier: \"A43512564ACBEF687924646CAFEFBDCAEDF4155125657\")\n```\n\n### Refresh receipt\n\nIf `Bundle.main.appStoreReceiptURL` is null, you need to create a refresh receipt request to obtain a receipt for a payment transaction.\n\n```\nDYFStore.default.refreshReceipt(onSuccess: {\n    self.storeReceipt()\n}) { (error) in\n    self.failToRefreshReceipt()\n}\n```\n\n### Notifications\n\n`DYFStore` sends notifications of `StoreKit` related events and extends `NSNotification` to provide relevant information. To receive them, add the observer to a `DYFStore` manager.\n\n#### Add the store observer\n\n```\nfunc addStoreObserver() {\n    NotificationCenter.default.addObserver(self, selector: #selector(processPurchaseNotification(_:)), name: DYFStore.purchasedNotification, object: nil)\n    NotificationCenter.default.addObserver(self, selector: #selector(processDownloadNotification(_:)), name: DYFStore.downloadedNotification, object: nil)\n}\n```\n\n#### Remove the store observer\n\nWhen the application exits, you need to remove the store observer.\n\n```\nfunc removeStoreObserver() {\n    NotificationCenter.default.removeObserver(self, name: DYFStore.purchasedNotification, object: nil)\n    NotificationCenter.default.removeObserver(self, name: DYFStore.downloadedNotification, object: nil)\n}\n```\n\n#### Payment transaction notifications\n\nPayment transaction notifications are sent after a payment has been requested or for each restored transaction.\n\n```\n@objc private func processPurchaseNotification(_ notification: Notification) {\n    self.sk_hideLoading()\n    self.purchaseInfo = (notification.object as! DYFStore.NotificationInfo)\n    switch self.purchaseInfo.state! {\n    case .purchasing:\n        self.sk_showLoading(\"Purchasing...\")\n        break\n    case .cancelled:\n        self.sendNotice(\"You cancel the purchase\")\n        break\n    case .failed:\n        self.sendNotice(String(format: \"An error occurred, \\(self.purchaseInfo.error!.code)\"))\n        break\n    case .succeeded, .restored:\n        self.completePayment()\n        break\n    case .restoreFailed:\n        self.sendNotice(String(format: \"An error occurred, \\(self.purchaseInfo.error!.code)\"))\n        break\n    case .deferred:\n        DYFStoreLog(\"Deferred\")\n        break\n    }\n}\n```\n\n#### Download notifications\n\n```\n@objc private func processDownloadNotification(_ notification: Notification) {\n    self.downloadInfo = (notification.object as! DYFStore.NotificationInfo)\n    switch self.downloadInfo.downloadState! {\n    case .started:\n        DYFStoreLog(\"The download started\")\n        break\n    case .inProgress:\n        DYFStoreLog(\"The download progress: \\(self.downloadInfo.downloadProgress)%%\")\n        break\n    case .cancelled:\n        DYFStoreLog(\"The download cancelled\")\n        break\n    case .failed:\n        DYFStoreLog(\"The download failed\")\n        break\n    case .succeeded:\n        DYFStoreLog(\"The download succeeded: 100%%\")\n        break\n    }\n}\n```\n\n### Receipt verification\n\n`DYFStore` doesn't perform receipt verification by default, but provides reference implementations. You can implement your own custom verification or use the reference verifier provided by the library.\n\nThe reference verifier is outlined below. For more info, check out the [wiki](https://github.com/itenfay/DYFStore/wiki/Receipt-verification).\n\n#### Reference verifier\n\nYou create and return a receipt verifier(`DYFStoreReceiptVerifier`) by using lazy loading.\n\n```\nlazy var receiptVerifier: DYFStoreReceiptVerifier = {\n    let verifier = DYFStoreReceiptVerifier()\n    verifier.delegate = self\n    return verifier\n}()\n```\n\nThe receipt verifier delegates receipt verification, enabling you to provide your own implementation using the `DYFStoreReceiptVerifierDelegate` protocol:\n\n```\npublic func verifyReceiptDidFinish(_ verifier: DYFStoreReceiptVerifier, didReceiveData data: [String : Any]) {}\n\npublic func verifyReceipt(_ verifier: DYFStoreReceiptVerifier, didFailWithError error: NSError) {}\n```\n\nYou can start verifying the in-app purchase receipt. \n\n```\n// Fetches the data of the bundle’s App Store receipt. \nlet data = receiptData\nor\nlet data = try? Data(contentsOf: DYFStore.receiptURL())\n\nself.receiptVerifier.verifyReceipt(data)\n\n// Only used for receipts that contain auto-renewable subscriptions.\n//self.receiptVerifier.verifyReceipt(data, sharedSecret: \"A43512564ACBEF687924646CAFEFBDCAEDF4155125657\")\n```\n\nIf security is a concern you might want to avoid using an open source verification logic, and provide your own custom verifier instead.\n\nIt is better to use your own server to obtain the parameters uploaded from the client to verify the receipt from the app store server (C -\u003e Uploaded Parameters -\u003e S -\u003e App Store S -\u003e S -\u003e Receive And Parse Data -\u003e C, C: client, S: server).\n\n### Finish transactions\n\nThe transaction can be finished only after the client and server adopt secure communication and data encryption and the receipt verification is passed. In this way, we can avoid refreshing orders and cracking in-app purchase. If we were unable to complete the verification, we want `StoreKit` to keep reminding us that there are still outstanding transactions.\n\n```\nDYFStore.default.finishTransaction(transaction)\n```\n\n\n## Transaction persistence\n\n`DYFStore` provides an optional reference implementation for storing transactions in `NSUserDefaults`(`DYFStoreUserDefaultsPersistence`). \n\nWhen the client crashes during the payment process, it is particularly important to store transaction information. When storekit notifies the uncompleted payment again, it takes the data directly from keychain and performs the receipt verification until the transaction is completed.\n\n### Store transaction\n\n```\nfunc storeReceipt() {\n    guard let url = DYFStore.receiptURL() else {\n        self.refreshReceipt()\n        return\n    }\n    do {\n        let data = try Data(contentsOf: url)\n        let info = self.purchaseInfo!\n        let persister = DYFStoreUserDefaultsPersistence()\n        \n        let tx = DYFStoreTransaction()\n        if info.state! == .succeeded {\n            tx.state = DYFStoreTransactionState.purchased.rawValue\n        } else if info.state! == .restored {\n            tx.state = DYFStoreTransactionState.restored.rawValue\n        }\n        \n        tx.productIdentifier = info.productIdentifier\n        tx.userIdentifier = info.userIdentifier\n        tx.transactionTimestamp = info.transactionDate?.timestamp()\n        tx.transactionIdentifier = info.transactionIdentifier\n        tx.originalTransactionTimestamp = info.originalTransactionDate?.timestamp()\n        tx.originalTransactionIdentifier = info.originalTransactionIdentifier\n        \n        tx.transactionReceipt = data.base64EncodedString()\n        persister.storeTransaction(tx)\n        \n        self.verifyReceipt(data)\n    } catch let error {\n        DYFStoreLog(\"error: \\(error.localizedDescription)\")\n        self.refreshReceipt()\n        return\n    }\n}\n```\n\n### Remove transaction\n\n```\nlet info = self.purchaseInfo!\nlet store = DYFStore.default\nlet persister = DYFStoreUserDefaultsPersistence()\nlet identifier = info.transactionIdentifier!\n\nif info.state! == .restored {\n    let tx = store.extractRestoredTransaction(identifier)\n    store.finishTransaction(tx)\n} else {\n    let tx = store.extractPurchasedTransaction(identifier)\n    // The transaction can be finished only after the receipt verification passed under the client and the server can adopt the communication of security and data encryption. In this way, we can avoid refreshing orders and cracking in-app purchase. If we were unable to complete the verification we want StoreKit to keep reminding us of the transaction.\n    store.finishTransaction(tx)\n}\n\npersister.removeTransaction(identifier)\n\nif let id = info.originalTransactionIdentifier {\n    persister.removeTransaction(id)\n}\n```\n\n\n## Requirements\n\n`DYFStore` requires `iOS 8.0` or above and `ARC`.\n\n\n## Demo\n\nTo learn more, please clone this project (`git clone https://github.com/itenfay/DYFStore.git`) to the local directory.\n\n\n## Feedback is welcome\n\nIf you notice any issue to create an issue. I will be happy to help you.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fitenfay%2Fdyfstore","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fitenfay%2Fdyfstore","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fitenfay%2Fdyfstore/lists"}