{"action":"created","comment":{"url":"https://api.github.com/repos/silverapp/silver/pulls/comments/212268355","pull_request_review_id":148857745,"id":212268355,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIxMjI2ODM1NQ==","diff_hunk":"@@ -0,0 +1,449 @@\n+---\n+title: Adding a new payment processor\n+description: This guide will explain the steps of adding a new payment processor to Silver.\n+---\n+\n+## Getting started\n+First, there needs to be a Python SDK for your Payment Processor, that allows you to connect and \n+communicate with it's services. If there isn't one, you should start by contacting the Payment \n+Processor's developers and ask them if they can provide one, or try implementing it yourself.\n+\n+In this guide, we'll name the SDK package `payment_processor_sdk` and it will be totally imaginary.\n+We could use a real SDK, but most have their quirks and ways of doing stuff, and that will only act\n+as a distraction. So you might consider also having a look at some real payment processor implementations\n+in [here](https://github.com/silverapp/).\n+\n+It's usually best to have a separate Django app or project for your Payment Processor.\n+You may call your app / project whatever you want, but in this guide we'll name it `payment_processor`.\n+We'll also assume you've named your main Silver project `silver_project`. You should obviously use\n+proper names instead.\n+\n+\n+### The Django app variant\n+This method is better if you want to get started faster. You can always decide to publish your app \n+as a separate Django project if you want.\n+\n+- Create your app\n+```bash\n+./manage.py startapp payment_processor\n+```\n+\n+- Add it to your INSTALLED_APPS in your settings file\n+```python\n+INTERNAL_APPS = [\n+    'silver',\n+    'payment_processor',\n+    # ...\n+]\n+```\n+\n+\n+### The Django project variant\n+This method is better if you want to keep your projects separated or publish your work from the \n+beginning.\n+\n+This method also assumes you have already created a virtualenv for the main project, the payment \n+processor project and installed Django there too.\n+\n+- Create your project\n+```bash\n+django_admin startproject payment_processor\n+```\n+\n+- Install it by linking it inside your main project's environment, where Silver is also installed\n+```bash\n+workon silver_project  # Change to your main project's environment\n+cd payment_processor  # Go to your payment_processor's directory\n+pip install -e .  # Install your payment_processor development project as a linked dependecy\n+```\n+\n+- Add the payment_processor to your INSTALLED_APPS in your silver_project's settings file\n+```python\n+INTERNAL_APPS = [\n+    'silver',\n+    'payment_processor',\n+    # ...\n+]\n+```\n+\n+\n+## Implementation\n+\n+### Deciding how your Payment Processor will look\n+\n+In your payment_processor app you should have the following structure. It's fine if you can't find \n+everything in there yet. You can complete it as we go:\n+\n+```\n+payment_processor\n+│\n+├── api\n+│   ├── __init__.py\n+│   └── views.py\n+├── migrations\n+│   ├── __init__.py\n+│   └── 0001_initial.py\n+├── models\n+│   ├── __init__.py\n+│   └── payment_methods.py\n+├── templates\n+│   └── forms\n+├── __init__.py\n+├── payment_processors.py\n+└── views.py\n+```\n+\n+In `payment_processors.py` you will create your Payment Processor class. You should inherit\n+the PaymentProcessorBase class and a mixin class based on what type of your Payment Processor will be.\n+For more info, you should read the [payment processor resource definition](Resources#payment-processor).\n+\n+We are going to \"partially implement\" a `triggered` Payment Processor.\n+That means the Transactions (payments) will be triggered from within Silver, and their status will \n+be mirrored to Silver either by polling or by webhooks if your payment processor service provides them.\n+\n+```python\n+# payment_processors.py\n+\n+import payment_processor_sdk\n+\n+from silver.payment_processors import PaymentProcessorBase\n+from silver.payment_processors.mixins import TriggeredProcessorMixin\n+\n+class TriggeredPaymentProcessor(PaymentProcessorBase, TriggeredProcessorMixin):\n+    template_slug = 'payment_processor_triggered'\n+```\n+\n+There's not much in there at the moment so we'll try to add a way of creating an external (real) \n+transaction. We can do that by implementing the `execute_transaction` method.\n+\n+```python\n+# payment_processors.py\n+# ...\n+\n+class TriggeredPaymentProcessor(PaymentProcessorBase, TriggeredProcessorMixin):\n+    template_slug = 'payment_processor_triggered'\n+\n+    def execute_transaction(self, transaction):\n+        \"\"\"\n+            :param transaction: A Silver Transaction object in pending state, that hasn't been \n+            executed before.\n+\n+            Creates a real, external transaction based on the given Silver transaction.\n+\n+            Warning: You should never call this method directly! Use the `process_transaction` \n+                     method instead, which will call this method.\n+                     However, if you need to call it directly, make sure the transaction hasn't been \n+                     executed before.\n+\n+            :return: True on success, False on failure.\n+        \"\"\"\n+\n+        # The following code is purely educational. Your code will probably look a lot different.\n+        # Start by creating your external transaction. You usually want to specify as many details\n+        # about the transaction as possible.\n+        transaction_result = payment_processor_sdk.create_transaction({\n+            'amount': transaction.amount,\n+            'currency': transaction.currency,\n+            'reference': str(transaction.uuid),\n+            # 'token': transaction.payment_method.token -- We'll discuss about this later.\n+        })\n+\n+        # Most payment processors don't necessarily execute (authorize, submit for settlement...) the\n+        # transaction on their own, when creating the transaction. Let's do that:\n+        transaction_result = payment_processor_sdk.execute(transaction_result.id)\n+\n+        # Update the Silver transaction to reflect the external's transaction new status\n+        self._update_transaction_status(transaction, transaction_result)\n+\n+    def _update_transaction_status(self, transaction, transaction_result):\n+        \"\"\"\n+            :param transaction: A silver transaction.\n+            :param transaction_response: A response object from the payment processor SDK.\n+            This method is not required by silver.\n+        \"\"\"\n+\n+        # You can do an additional check to make sure the two objects correlate:\n+        if not str(transaction.uuid) == transaction_result.reference:\n+            raise ValueError(\"The transaction and transaction_response don't match.\")\n+\n+        # First we update the Silver transaction's external reference and status\n+        transaction.external_reference = transaction_result.id\n+        status = transaction_result.status\n+\n+        transaction.data.update({\n+            'status': status,\n+            'id': transaction_result.id\n+        })\n+        \n+        # Then we transition the Silver transaction to a new state if needed:\n+        if status == 'settled':\n+            transaction.settle()\n+        elif status == 'failed':\n+            transaction.fail()\n+        elif status == 'canceled':  # sometimes called voided\n+            transaction.cancel()\n+        elif status == 'refunded':\n+            transaction.refund()\n+\n+        transaction.save()\n+```\n+\n+You can specify codes and reason messages for the sad path transitions (fail, cancel, refund).\n+Silver has some predefined codes to which, if you wish, you can convert the codes provided by the SDK.\n+\n+```python\n+from silver.models.transactions.codes import FAIL_CODES, CANCEL_CODES, REFUND_CODES\n+\n+def _convert_to_silver_code(sdk_code):\n+    sdk_to_silver_codes = {}\n+    return sdk_to_silver_codes[sdk_code]\n+\n+def _update_transaction_status(self, transaction, transaction_result):\n+    # ...\n+    fail_code = _convert_to_silver_code(transaction_result.error_code)\n+    transaction.fail(fail_code=fail_code, fail_reason=transaction_result.fail_reason)\n+\n+    # Later if you want to send an email to your customer for example, you can use the generic messages\n+    # provided by silver\n+    print(FAIL_CODES[transaction.fail_code]['message'],\n+          FAIL_CODES[transaction.fail_code].get('solve_message', 'Contact our support'))\n+```\n+\n+### Payment methods\n+\n+You can implement a Payment Method class if you want to add specific payment method logic:\n+```python\n+# payment_processors.py\n+# ...\n+from .models.payment_methods import CustomPaymentMethod\n+\n+class TriggeredPaymentProcessor(PaymentProcessorBase, TriggeredProcessorMixin):\n+    payment_method_class = CustomPaymentMethod\n+    #...\n+```\n+\n+```python\n+# models/payment_methods.py\n+# ...\n+from silver.models import PaymentMethod\n+\n+class CustomPaymentMethod(PaymentMethod):\n+    class Meta:\n+        proxy = True\n+    \n+    @property\n+    def token(self):\n+        return self.decrypt_data(self.data.get('token'))\n+\n+    @token.setter\n+    def token(self, value):\n+        self.data['token'] = self.encrypt_data(value)  # self.encrypt_data is already implemented\n+```\n+\n+You might have noticed that earlier, when we implemented `execute_transaction`, we mentioned a token. \n+Most payment processors will provide you with a token, because you are not supposed to handle the\n+customer's payment method info, so you will use the token to refer to a payment method.\n+\n+The token is useful if the customer wants to save his payment method info for further or recurring \n+payments. Read about how you can obtain it in the [payment operation section](#the-payment-operation).\n+\n+\n+### The payment operation\n+\n+This section tries to explain how the customer's payment operation usually goes like.\n+\n+1. The customer usually clicks a Pay button somewhere in a different application, which will trigger a\n+[create request on the Silver's `/transactions` endpoint](Transactions#create-a-transaction).\n+2. The transaction is created, and the response payload contains a field called `pay_url`, which\n+is a Silver URL to a view that will act as a payment page, which most likely you'll also have to ","path":"docs/documentation/guides/adding-a-new-payment-processor.md","position":258,"original_position":258,"commit_id":"aa9c8e4b65bb6fe467d7915647b8c5e30ed0e2d2","original_commit_id":"aa9c8e4b65bb6fe467d7915647b8c5e30ed0e2d2","user":{"login":"vtemian","id":639771,"node_id":"MDQ6VXNlcjYzOTc3MQ==","avatar_url":"https://avatars0.githubusercontent.com/u/639771?v=4","gravatar_id":"","url":"https://api.github.com/users/vtemian","html_url":"https://github.com/vtemian","followers_url":"https://api.github.com/users/vtemian/followers","following_url":"https://api.github.com/users/vtemian/following{/other_user}","gists_url":"https://api.github.com/users/vtemian/gists{/gist_id}","starred_url":"https://api.github.com/users/vtemian/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vtemian/subscriptions","organizations_url":"https://api.github.com/users/vtemian/orgs","repos_url":"https://api.github.com/users/vtemian/repos","events_url":"https://api.github.com/users/vtemian/events{/privacy}","received_events_url":"https://api.github.com/users/vtemian/received_events","type":"User","site_admin":false},"body":"`is a Silver URL that points to a view`","created_at":"2018-08-23T11:06:10Z","updated_at":"2018-08-23T11:17:01Z","html_url":"https://github.com/silverapp/silver/pull/611#discussion_r212268355","pull_request_url":"https://api.github.com/repos/silverapp/silver/pulls/611","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/silverapp/silver/pulls/comments/212268355"},"html":{"href":"https://github.com/silverapp/silver/pull/611#discussion_r212268355"},"pull_request":{"href":"https://api.github.com/repos/silverapp/silver/pulls/611"}}},"pull_request":{"url":"https://api.github.com/repos/silverapp/silver/pulls/611","id":193626946,"node_id":"MDExOlB1bGxSZXF1ZXN0MTkzNjI2OTQ2","html_url":"https://github.com/silverapp/silver/pull/611","diff_url":"https://github.com/silverapp/silver/pull/611.diff","patch_url":"https://github.com/silverapp/silver/pull/611.patch","issue_url":"https://api.github.com/repos/silverapp/silver/issues/611","number":611,"state":"open","locked":false,"title":"Payment processor guide (closes #606)","user":{"login":"bogdanpetrea","id":8372264,"node_id":"MDQ6VXNlcjgzNzIyNjQ=","avatar_url":"https://avatars1.githubusercontent.com/u/8372264?v=4","gravatar_id":"","url":"https://api.github.com/users/bogdanpetrea","html_url":"https://github.com/bogdanpetrea","followers_url":"https://api.github.com/users/bogdanpetrea/followers","following_url":"https://api.github.com/users/bogdanpetrea/following{/other_user}","gists_url":"https://api.github.com/users/bogdanpetrea/gists{/gist_id}","starred_url":"https://api.github.com/users/bogdanpetrea/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bogdanpetrea/subscriptions","organizations_url":"https://api.github.com/users/bogdanpetrea/orgs","repos_url":"https://api.github.com/users/bogdanpetrea/repos","events_url":"https://api.github.com/users/bogdanpetrea/events{/privacy}","received_events_url":"https://api.github.com/users/bogdanpetrea/received_events","type":"User","site_admin":false},"body":"","created_at":"2018-06-08T14:13:59Z","updated_at":"2018-08-23T11:17:00Z","closed_at":null,"merged_at":null,"merge_commit_sha":"a06b80f06582cf434c3d877f8d020e410ec21789","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/silverapp/silver/pulls/611/commits","review_comments_url":"https://api.github.com/repos/silverapp/silver/pulls/611/comments","review_comment_url":"https://api.github.com/repos/silverapp/silver/pulls/comments{/number}","comments_url":"https://api.github.com/repos/silverapp/silver/issues/611/comments","statuses_url":"https://api.github.com/repos/silverapp/silver/statuses/aa9c8e4b65bb6fe467d7915647b8c5e30ed0e2d2","head":{"label":"silverapp:payment-processor-example","ref":"payment-processor-example","sha":"aa9c8e4b65bb6fe467d7915647b8c5e30ed0e2d2","user":{"login":"silverapp","id":25032711,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI1MDMyNzEx","avatar_url":"https://avatars2.githubusercontent.com/u/25032711?v=4","gravatar_id":"","url":"https://api.github.com/users/silverapp","html_url":"https://github.com/silverapp","followers_url":"https://api.github.com/users/silverapp/followers","following_url":"https://api.github.com/users/silverapp/following{/other_user}","gists_url":"https://api.github.com/users/silverapp/gists{/gist_id}","starred_url":"https://api.github.com/users/silverapp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/silverapp/subscriptions","organizations_url":"https://api.github.com/users/silverapp/orgs","repos_url":"https://api.github.com/users/silverapp/repos","events_url":"https://api.github.com/users/silverapp/events{/privacy}","received_events_url":"https://api.github.com/users/silverapp/received_events","type":"Organization","site_admin":false},"repo":{"id":24887812,"node_id":"MDEwOlJlcG9zaXRvcnkyNDg4NzgxMg==","name":"silver","full_name":"silverapp/silver","owner":{"login":"silverapp","id":25032711,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI1MDMyNzEx","avatar_url":"https://avatars2.githubusercontent.com/u/25032711?v=4","gravatar_id":"","url":"https://api.github.com/users/silverapp","html_url":"https://github.com/silverapp","followers_url":"https://api.github.com/users/silverapp/followers","following_url":"https://api.github.com/users/silverapp/following{/other_user}","gists_url":"https://api.github.com/users/silverapp/gists{/gist_id}","starred_url":"https://api.github.com/users/silverapp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/silverapp/subscriptions","organizations_url":"https://api.github.com/users/silverapp/orgs","repos_url":"https://api.github.com/users/silverapp/repos","events_url":"https://api.github.com/users/silverapp/events{/privacy}","received_events_url":"https://api.github.com/users/silverapp/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/silverapp/silver","description":"Automated billing and payments for Django with a REST API","fork":false,"url":"https://api.github.com/repos/silverapp/silver","forks_url":"https://api.github.com/repos/silverapp/silver/forks","keys_url":"https://api.github.com/repos/silverapp/silver/keys{/key_id}","collaborators_url":"https://api.github.com/repos/silverapp/silver/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/silverapp/silver/teams","hooks_url":"https://api.github.com/repos/silverapp/silver/hooks","issue_events_url":"https://api.github.com/repos/silverapp/silver/issues/events{/number}","events_url":"https://api.github.com/repos/silverapp/silver/events","assignees_url":"https://api.github.com/repos/silverapp/silver/assignees{/user}","branches_url":"https://api.github.com/repos/silverapp/silver/branches{/branch}","tags_url":"https://api.github.com/repos/silverapp/silver/tags","blobs_url":"https://api.github.com/repos/silverapp/silver/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/silverapp/silver/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/silverapp/silver/git/refs{/sha}","trees_url":"https://api.github.com/repos/silverapp/silver/git/trees{/sha}","statuses_url":"https://api.github.com/repos/silverapp/silver/statuses/{sha}","languages_url":"https://api.github.com/repos/silverapp/silver/languages","stargazers_url":"https://api.github.com/repos/silverapp/silver/stargazers","contributors_url":"https://api.github.com/repos/silverapp/silver/contributors","subscribers_url":"https://api.github.com/repos/silverapp/silver/subscribers","subscription_url":"https://api.github.com/repos/silverapp/silver/subscription","commits_url":"https://api.github.com/repos/silverapp/silver/commits{/sha}","git_commits_url":"https://api.github.com/repos/silverapp/silver/git/commits{/sha}","comments_url":"https://api.github.com/repos/silverapp/silver/comments{/number}","issue_comment_url":"https://api.github.com/repos/silverapp/silver/issues/comments{/number}","contents_url":"https://api.github.com/repos/silverapp/silver/contents/{+path}","compare_url":"https://api.github.com/repos/silverapp/silver/compare/{base}...{head}","merges_url":"https://api.github.com/repos/silverapp/silver/merges","archive_url":"https://api.github.com/repos/silverapp/silver/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/silverapp/silver/downloads","issues_url":"https://api.github.com/repos/silverapp/silver/issues{/number}","pulls_url":"https://api.github.com/repos/silverapp/silver/pulls{/number}","milestones_url":"https://api.github.com/repos/silverapp/silver/milestones{/number}","notifications_url":"https://api.github.com/repos/silverapp/silver/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/silverapp/silver/labels{/name}","releases_url":"https://api.github.com/repos/silverapp/silver/releases{/id}","deployments_url":"https://api.github.com/repos/silverapp/silver/deployments","created_at":"2014-10-07T11:39:38Z","updated_at":"2018-08-11T08:02:07Z","pushed_at":"2018-08-23T11:05:48Z","git_url":"git://github.com/silverapp/silver.git","ssh_url":"git@github.com:silverapp/silver.git","clone_url":"https://github.com/silverapp/silver.git","svn_url":"https://github.com/silverapp/silver","homepage":"https://www.presslabs.org/silver/","size":3039,"stargazers_count":177,"watchers_count":177,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":46,"mirror_url":null,"archived":false,"open_issues_count":77,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":46,"open_issues":77,"watchers":177,"default_branch":"master"}},"base":{"label":"silverapp:master","ref":"master","sha":"db7c89d3a555e735a5179428a4f6d7b04d294c8e","user":{"login":"silverapp","id":25032711,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI1MDMyNzEx","avatar_url":"https://avatars2.githubusercontent.com/u/25032711?v=4","gravatar_id":"","url":"https://api.github.com/users/silverapp","html_url":"https://github.com/silverapp","followers_url":"https://api.github.com/users/silverapp/followers","following_url":"https://api.github.com/users/silverapp/following{/other_user}","gists_url":"https://api.github.com/users/silverapp/gists{/gist_id}","starred_url":"https://api.github.com/users/silverapp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/silverapp/subscriptions","organizations_url":"https://api.github.com/users/silverapp/orgs","repos_url":"https://api.github.com/users/silverapp/repos","events_url":"https://api.github.com/users/silverapp/events{/privacy}","received_events_url":"https://api.github.com/users/silverapp/received_events","type":"Organization","site_admin":false},"repo":{"id":24887812,"node_id":"MDEwOlJlcG9zaXRvcnkyNDg4NzgxMg==","name":"silver","full_name":"silverapp/silver","owner":{"login":"silverapp","id":25032711,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI1MDMyNzEx","avatar_url":"https://avatars2.githubusercontent.com/u/25032711?v=4","gravatar_id":"","url":"https://api.github.com/users/silverapp","html_url":"https://github.com/silverapp","followers_url":"https://api.github.com/users/silverapp/followers","following_url":"https://api.github.com/users/silverapp/following{/other_user}","gists_url":"https://api.github.com/users/silverapp/gists{/gist_id}","starred_url":"https://api.github.com/users/silverapp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/silverapp/subscriptions","organizations_url":"https://api.github.com/users/silverapp/orgs","repos_url":"https://api.github.com/users/silverapp/repos","events_url":"https://api.github.com/users/silverapp/events{/privacy}","received_events_url":"https://api.github.com/users/silverapp/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/silverapp/silver","description":"Automated billing and payments for Django with a REST API","fork":false,"url":"https://api.github.com/repos/silverapp/silver","forks_url":"https://api.github.com/repos/silverapp/silver/forks","keys_url":"https://api.github.com/repos/silverapp/silver/keys{/key_id}","collaborators_url":"https://api.github.com/repos/silverapp/silver/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/silverapp/silver/teams","hooks_url":"https://api.github.com/repos/silverapp/silver/hooks","issue_events_url":"https://api.github.com/repos/silverapp/silver/issues/events{/number}","events_url":"https://api.github.com/repos/silverapp/silver/events","assignees_url":"https://api.github.com/repos/silverapp/silver/assignees{/user}","branches_url":"https://api.github.com/repos/silverapp/silver/branches{/branch}","tags_url":"https://api.github.com/repos/silverapp/silver/tags","blobs_url":"https://api.github.com/repos/silverapp/silver/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/silverapp/silver/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/silverapp/silver/git/refs{/sha}","trees_url":"https://api.github.com/repos/silverapp/silver/git/trees{/sha}","statuses_url":"https://api.github.com/repos/silverapp/silver/statuses/{sha}","languages_url":"https://api.github.com/repos/silverapp/silver/languages","stargazers_url":"https://api.github.com/repos/silverapp/silver/stargazers","contributors_url":"https://api.github.com/repos/silverapp/silver/contributors","subscribers_url":"https://api.github.com/repos/silverapp/silver/subscribers","subscription_url":"https://api.github.com/repos/silverapp/silver/subscription","commits_url":"https://api.github.com/repos/silverapp/silver/commits{/sha}","git_commits_url":"https://api.github.com/repos/silverapp/silver/git/commits{/sha}","comments_url":"https://api.github.com/repos/silverapp/silver/comments{/number}","issue_comment_url":"https://api.github.com/repos/silverapp/silver/issues/comments{/number}","contents_url":"https://api.github.com/repos/silverapp/silver/contents/{+path}","compare_url":"https://api.github.com/repos/silverapp/silver/compare/{base}...{head}","merges_url":"https://api.github.com/repos/silverapp/silver/merges","archive_url":"https://api.github.com/repos/silverapp/silver/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/silverapp/silver/downloads","issues_url":"https://api.github.com/repos/silverapp/silver/issues{/number}","pulls_url":"https://api.github.com/repos/silverapp/silver/pulls{/number}","milestones_url":"https://api.github.com/repos/silverapp/silver/milestones{/number}","notifications_url":"https://api.github.com/repos/silverapp/silver/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/silverapp/silver/labels{/name}","releases_url":"https://api.github.com/repos/silverapp/silver/releases{/id}","deployments_url":"https://api.github.com/repos/silverapp/silver/deployments","created_at":"2014-10-07T11:39:38Z","updated_at":"2018-08-11T08:02:07Z","pushed_at":"2018-08-23T11:05:48Z","git_url":"git://github.com/silverapp/silver.git","ssh_url":"git@github.com:silverapp/silver.git","clone_url":"https://github.com/silverapp/silver.git","svn_url":"https://github.com/silverapp/silver","homepage":"https://www.presslabs.org/silver/","size":3039,"stargazers_count":177,"watchers_count":177,"language":"Python","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":46,"mirror_url":null,"archived":false,"open_issues_count":77,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":46,"open_issues":77,"watchers":177,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/silverapp/silver/pulls/611"},"html":{"href":"https://github.com/silverapp/silver/pull/611"},"issue":{"href":"https://api.github.com/repos/silverapp/silver/issues/611"},"comments":{"href":"https://api.github.com/repos/silverapp/silver/issues/611/comments"},"review_comments":{"href":"https://api.github.com/repos/silverapp/silver/pulls/611/comments"},"review_comment":{"href":"https://api.github.com/repos/silverapp/silver/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/silverapp/silver/pulls/611/commits"},"statuses":{"href":"https://api.github.com/repos/silverapp/silver/statuses/aa9c8e4b65bb6fe467d7915647b8c5e30ed0e2d2"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"vtemian"}}