個人開発のサービスを売買できる「個人開発のフリマ」を作りました

![title.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116375/c89a0be3-de41-4b18-4768-88dc8a5d7af4.png)

個人開発のフリマは、個人開発やスタートアップで開発したプロダクトをフリマ感覚で売買できるサービスです。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">個人開発のフリマ( <a href="https://t.co/pBm7hbuxOI">https://t.co/pBm7hbuxOI</a> )が始まります！個人開発で作ったサービスを売ったり、買ったりできるフリマサイトです。放置してるサービスなどある方、もしかしたら売れるかもしれませんよ‼️ ぜひ、登録してみてください☺️</p>&mdash; 個人開発のコミュニティ『開発会議』 (@devtalkjp) <a href="https://twitter.com/devtalkjp/status/1132869969325125632?ref_src=twsrc%5Etfw">2019年5月27日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


以前、[個人開発者の知見を集めたい！「開発会議」を作りました](https://qiita.com/svfreerider/items/def03b636df78eeea0e6)を投稿させていただき、200名以上の個人開発者の方々にご登録いただき、より良いサービスづくりをしていく為にインタビューを始めました。そのお話をお伺いする中で、自分のサービスで食べていく以外に「サービスの売却」という選択肢を視野に入れていることが分かりました。

また、私自身も以前、開発してバズったサービスを売却しようと考えた時がありました。しかし、従来のWebメディア売買を主要取引としたサイト群では審査に落ちてしまい、結果的に商機を逃してしまったのかなと思っています。

## 何ができるの

ズバリ、売ったり買ったりです。

## 開発環境

- Rails
- Heroku
- CloudFlare
- Amazon s3
- Amazon SES

## 売買スキームの実装

今回の売買では決済は実装していません。いずれ実装するかもしれませんが、まずは銀行口座によるマニュアルの振込も想定しています。交渉の申し込みから、契約内容の締結までを説明していきたいと思います。

### 交渉に関連するモデル

```ruby:models/user.rb
class User < ApplicationRecord
  has_many :businesses, dependent: :destroy

  has_many :buy_negotiations,  class_name: "Negotiation",
                                  foreign_key: "buyer_id",
                                  dependent: :destroy
  has_many :sell_negotiations, class_name: "Negotiation",
                                  foreign_key: "seller_id",
                                  dependent:   :destroy
  has_many :buy_content, through: :buy_negotiations,  source: :buyer
  has_many :sell_content, through: :sell_negotiations, source: :seller
  has_many :messages
end
```

```ruby:models/business.rb
class Business < ApplicationRecord
  belongs_to :user
  has_many :negotiations
end
```

```ruby:models/negotiation.rb
class Negotiation < ApplicationRecord
  before_create :set_slug

  belongs_to :business
  has_many :messages

  belongs_to :buyer, class_name: "User"
  belongs_to :seller, class_name: "User"
  
  validates :buyer_id, presence: true
  validates :seller_id, presence: true

  def to_param
    slug
  end

  private

  def set_slug
    loop do
      self.slug = SecureRandom.uuid
      break unless Negotiation.where(slug: slug).exists?
    end
  end
end
```

```ruby:models/message.rb
class Message < ApplicationRecord
  belongs_to :user
  belongs_to :negotiation
end
```

交渉（negotiation）は、URLを念の為スラッグで作るようにしています。

### 1) 交渉の申し込み
![frima.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116375/9e9c36f7-8289-4f00-ba07-9498f2822f97.png)

```erb:views/businesses/show.html.erb
<%= form_with model: @negotiation, url: negotiations_path, local: true do |f| %>
  <%= f.hidden_field :business_id, value: @business.id %>
  <%= f.hidden_field :seller_id, value: @business.user.id %>
  <%= f.hidden_field :buyer_id, value: current_user.id %>
  <%= f.submit "売却の交渉を始める", class: "btn btnPrimary btnStyle" %>
<% end %>
```

```ruby:controllers/businesses_controller.rb
  def show
・・・
    @negotiation = @business.negotiations.build
  end
```

Businessに紐づいた、交渉（negotiation）を作っていきます。

```ruby:controllers/negotiations_controller.rb
  def create
    @negotiation = Negotiation.create(create_params)
    if @negotiation.save
      NegotiationMailer.send_notification_to_seller(@negotiation).deliver
      flash[:success] = "交渉が始まりました！"
      redirect_to @negotiation
    else
      flash[:alert] = "交渉の開始に失敗しました。"
      redirect_to root_path
    end
  end
```
交渉が始まったら、売り手側にメールが飛ぶように設定しています。

### 2) 交渉
![frimafrima.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116375/e2be40de-c94e-3da8-c7cc-7f20ba40acb2.png)

```erb:views/negotiations/show.html.erb
・・・
    <div class="chatBox">
      <%= render partial: 'messages/form', locals: { message: @message, negotiation: @negotiation, negotiation_id: @negotiation.id } %>
    </div>
    <div id="comments_area">
       <%= render partial: 'messages/index', locals: { messages: @messages } %>
    </div>
・・・
```

```erb:views/messages/_form.html.erb
<%= form_with(model: [negotiation, message] ) do |form| %>
  <div class="chatInput">
    <%= form.text_area :content, rows: "3" %>
  </div>
  <div class="chatHead">
    <%= form.submit "送信する" %>
  </div>
<% end %>

```

```erb:views/messages/_index.html.erb
<ul>
  <% messages.each do |message| %>
    <% unless message.id.nil? %>
      <li>
        <div class="msg">
          <div class="flexContainer">
            <div class="icon">
              <%= image_tag message.user.image.to_s %>
            </div>
            <div class="text">
              <div class="name">
                <span><%= message.user.username %></span>
              </div>
              <div class="content">
                <p><%= message.content %></p>
              </div>
            </div>
          </div>
        </div>
      </li>
    <% end %>
  <% end %>
</ul>
```

```erb:views/messages/index.js.erb
$("#comments_area").html("<%= j(render 'index', { messages: @message.negotiation.messages }) %>")
$("textarea").val('')
```

メッセージのやり取りはリアルタイム性を意識して、Firebaseなどの導入も考えたのですが、最初は実用最低限でいいかなと思って、普通のJSでやりました。

```ruby:controllers/negotiations_controller.rb
  def show
    @negotiation = Negotiation.find_by_slug(params[:slug])
    @message = Message.new
    @messages = @negotiation.messages
  end
```

```ruby:controllers/messages_controller.rb
  def create
    @message = @negotiation.messages.build(create_params)
    @message.user_id = current_user.id
    if @message.save
      MessageMailer.send_notification_to_receiver(@message).deliver
      render :index
    end
  end
```

`negotiation`の綴りを何回かタイポして、エラーが起こりました。
モデル名はもっと、なるべくシンプルな方がいいですね。

サービスの使い方は[Crieitさんに投稿したのでこちら](https://crieit.net/posts/2afc452d982a30ccf489401d2c633b11)を参考にしてください。

いずれ、誰かが作るサービスだなと思っていましたが、であれば気づいた自分がまずトライすべきだなと思って、見切りでリリースしていました。すぐに売れることはなくても、とりあえず登録しておけば、忘れた頃に売れるかもしれません！

まずは、自分が作ったサービスに「値付け」から始めてみませんか？